blob: 8f2888b6634e3fc630d4987105ca727914796352 [file] [log] [blame]
Andrew Brown3831baf2015-01-19 13:38:52 -08001/*
andrewsbrown533c6ef2015-03-03 16:08:41 -08002 * jndn-mock
Andrew Brownfe6c21c2016-08-24 16:28:54 -07003 * Copyright (c) 2016, Intel Corporation.
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 Afanasyev522327e2016-02-19 19:40:31 -080016import net.named_data.jndn.ControlParameters;
17import net.named_data.jndn.ControlResponse;
18import net.named_data.jndn.Data;
19import net.named_data.jndn.Face;
20import net.named_data.jndn.Interest;
21import net.named_data.jndn.Name;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080022import net.named_data.jndn.encoding.EncodingException;
23import net.named_data.jndn.encoding.TlvWireFormat;
24import net.named_data.jndn.encoding.tlv.Tlv;
25import net.named_data.jndn.encoding.tlv.TlvDecoder;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080026import net.named_data.jndn.security.KeyChain;
27import net.named_data.jndn.security.SecurityException;
Alexander Afanasyev288fcc92018-07-24 17:28:14 -040028import net.named_data.jndn.security.pib.PibImpl;
29import net.named_data.jndn.security.tpm.TpmBackEnd;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080030import net.named_data.jndn.transport.Transport;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080031
andrewsbrown1f28bcf2015-04-20 13:29:20 -070032import java.nio.ByteBuffer;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080033import java.util.ArrayList;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080034import java.util.List;
Andrew Brown58191872016-02-05 22:16:57 -080035import java.util.logging.Level;
Andrew Brownec1b0d02015-02-21 13:11:42 -080036import java.util.logging.Logger;
Alexander Afanasyev8e9330f2016-01-25 19:13:40 -080037
Andrew Brown58191872016-02-05 22:16:57 -080038/**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080039 * A client-side face for unit testing.
andrewsbrown8e517fa2016-02-12 15:51:19 -080040 *
Andrew Brownc9ba5502016-08-25 09:40:53 -070041 * @author Alexander Afanasyev, aa@cs.ucla.edu
42 * @author Andrew Brown, andrew.brown@intel.com
Andrew Brown3831baf2015-01-19 13:38:52 -080043 */
Andrew Brown58191872016-02-05 22:16:57 -080044public class MockFace extends Face {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080045 /**
46 * Interests sent out of this MockFace.
47 * <p/>
48 * Sent Interests are appended to this container if options.enablePacketLogger
49 * is true. User of this class is responsible for cleaning up the container,
50 * if necessary. After .expressInterest, .processEvents must be called before
51 * the Interest would show up here.
52 */
53 public final List<Interest> sentInterests = new ArrayList<>();
Andrew Brown58191872016-02-05 22:16:57 -080054
andrewsbrown8e517fa2016-02-12 15:51:19 -080055 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080056 * Data sent out of this MockFace.
57 * <p/>
58 * Sent Data are appended to this container if options.enablePacketLogger is
59 * true. User of this class is responsible for cleaning up the container, if
60 * necessary. After .put, .processEvents must be called before the Data would
61 * show up here.
62 */
63 public final List<Data> sentData = new ArrayList<>();
64
65 /**
66 * Emits whenever an Interest is sent.
67 * <p/>
68 * After .expressInterest, .processEvents must be called before this signal
69 * would be emitted.
70 */
71 public final List<SignalOnSendInterest> onSendInterest = new ArrayList<>();
72
73 /**
74 * Emits whenever a Data packet is sent.
75 * <p/>
76 * After .putData, .processEvents must be called before this signal would be
77 * emitted.
78 */
79 public final List<SignalOnSendData> onSendData = new ArrayList<>();
80
81 private static final Logger LOGGER = Logger.getLogger(MockFace.class.getName());
andrewsbrown62c29122016-05-02 15:58:06 -070082 private MockTransport transport;
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080083 private KeyChain keyChain;
84
85 /////////////////////////////////////////////////////////////////////////////
86
87 /**
88 * API for handling {@link Interest}s.
andrewsbrown8e517fa2016-02-12 15:51:19 -080089 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080090 public interface SignalOnSendInterest {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080091 /**
92 * Callback called when an Interest is sent out through face (towards NFD).
93 * @param interest interest being sent out
94 */
Alexander Afanasyev288fcc92018-07-24 17:28:14 -040095 void emit(Interest interest);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080096 }
97
andrewsbrown8e517fa2016-02-12 15:51:19 -080098 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080099 * API for handling {@link Data}s.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800100 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800101 public interface SignalOnSendData {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800102 /**
103 * Callback called when a Data is sent out through face (towards NFD).
104 *
105 * @param data data being sent out
106 */
Alexander Afanasyev288fcc92018-07-24 17:28:14 -0400107 void emit(Data data);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800108 }
109
110 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800111 * Options for MockFace.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800112 */
Andrew Brown58191872016-02-05 22:16:57 -0800113 public static class Options {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800114 private boolean enablePacketLogging = false;
115 private boolean enableRegistrationReply = false;
116
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800117 /**
118 * @return true if packet logging is enabled
119 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800120 public boolean isEnablePacketLogging() {
121 return enablePacketLogging;
122 }
Andrew Brown58191872016-02-05 22:16:57 -0800123
124 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800125 * Enable/disable packet logging.
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800126 *
127 * @param enablePacketLogging If true, packets sent out of MockFace will be appended to a container
128 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800129 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800130 public Options setEnablePacketLogging(final boolean enablePacketLogging) {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800131 this.enablePacketLogging = enablePacketLogging;
132 return this;
133 }
134
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800135 /**
136 * @return true if prefix registration mocking is enabled
137 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800138 public boolean isEnableRegistrationReply() {
139 return enableRegistrationReply;
140 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800141
142 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800143 * Enable/disable prefix registration mocking.
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800144 *
145 * @param enableRegistrationReply If true, prefix registration command will be automatically replied with a
146 * successful response
147 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800148 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800149 public Options setEnableRegistrationReply(final boolean enableRegistrationReply) {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800150 this.enableRegistrationReply = enableRegistrationReply;
151 return this;
152 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800153 }
154
andrewsbrown8e517fa2016-02-12 15:51:19 -0800155 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800156 * Default options.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800157 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800158 public static final Options DEFAULT_OPTIONS = new Options()
159 .setEnablePacketLogging(true)
160 .setEnableRegistrationReply(true);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800161
162 /**
andrewsbrown8e517fa2016-02-12 15:51:19 -0800163 * Create MockFace that logs packets in {@link #sentInterests} and
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800164 * {@link #sentData} and emulates NFD prefix registration.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800165 *
166 * @throws SecurityException should not be thrown by this test class
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800167 */
Andrew Brownc9ba5502016-08-25 09:40:53 -0700168 public MockFace() {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800169 this(DEFAULT_OPTIONS);
170 }
171
172 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800173 * Create MockFace with the specified options.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800174 * <p>
175 * To create Face that does not log packets:
176 * <pre>
177 * new MockFace(new Options());
178 * // use onSendInterest.add(handler) and onSendData.add(handler)
179 * // to add custom logic when Interest or Data packet are sent
andrewsbrown62c29122016-05-02 15:58:06 -0700180 * // from the upper level (to callback)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800181 * </pre>
182 *
183 * To create Face that just logs packets in sentInterests and sentData:
184 * <pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800185 * new MockFace(new Options(){ enablePacketLogging = true; });
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800186 * </pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800187 *
188 * @param options see {@link Options}
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800189 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800190 public MockFace(final Options options) {
andrewsbrown62c29122016-05-02 15:58:06 -0700191 super(new MockTransport(), null);
192 transport = (MockTransport) node_.getTransport();
andrewsbrown8e517fa2016-02-12 15:51:19 -0800193 transport.setOnSendBlock(new OnIncomingPacket());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800194
andrewsbrown8e517fa2016-02-12 15:51:19 -0800195 try {
196 keyChain = MockKeyChain.configure(new Name("/mock/key"));
197 setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
198 } catch (SecurityException ex) {
199 LOGGER.log(Level.SEVERE, "Unexpected error in MockKeyChain; this class should never throw", ex);
200 throw new Error(ex);
201 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800202
Alexander Afanasyev4c3de792016-02-19 20:18:41 -0800203 if (options.isEnablePacketLogging()) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800204 onSendInterest.add(new SignalOnSendInterest() {
205 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800206 public void emit(final Interest interest) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800207 sentInterests.add(interest);
208 }
209 });
210
211 onSendData.add(new SignalOnSendData() {
212 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800213 public void emit(final Data data) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800214 sentData.add(data);
215 }
216 });
217 }
218
Alexander Afanasyev4c3de792016-02-19 20:18:41 -0800219 if (options.isEnableRegistrationReply()) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800220 onSendInterest.add(new OnPrefixRegistration());
221 }
222 }
223
224 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800225 * Route incoming packets to the correct callbacks.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800226 */
andrewsbrown62c29122016-05-02 15:58:06 -0700227 private class OnIncomingPacket implements MockTransport.OnSendBlockSignal {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800228 /**
229 * {@inheritDoc}
230 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800231 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800232 public void emit(final ByteBuffer buffer) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800233 // @todo Implement NDNLP processing
234
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800235 try {
236 if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
237 TlvDecoder decoder = new TlvDecoder(buffer);
238 if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
239 Interest interest = new Interest();
240 interest.wireDecode(buffer, TlvWireFormat.get());
andrewsbrown8e517fa2016-02-12 15:51:19 -0800241
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800242 for (SignalOnSendInterest signal : onSendInterest) {
243 signal.emit(interest);
244 }
245 } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
246 Data data = new Data();
247 data.wireDecode(buffer, TlvWireFormat.get());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800248
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800249 for (SignalOnSendData signal : onSendData) {
250 signal.emit(data);
251 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800252 }
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800253 } else {
254 LOGGER.info("Received an unknown packet");
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800255 }
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800256 } catch (EncodingException e) {
andrewsbrown62c29122016-05-02 15:58:06 -0700257 LOGGER.log(Level.INFO, "Failed to decodeParameters incoming packet", e);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800258 }
259 }
260 }
261
262 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800263 * Handle prefix registration requests.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800264 */
265 private class OnPrefixRegistration implements SignalOnSendInterest {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800266 private static final int STATUS_CODE_OK = 200;
267 private static final int CONTROL_PARAMETERS_NAME_OFFSET = -5;
268 private static final int CONTROL_COMMAND_NAME_OFFSET = 3;
andrewsbrown8e517fa2016-02-12 15:51:19 -0800269
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800270 /**
271 * {@inheritDoc}
272 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800273 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800274 public void emit(final Interest interest) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800275 final Name localhostRegistration = new Name("/localhost/nfd/rib");
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800276 if (!interest.getName().getPrefix(localhostRegistration.size()).equals(localhostRegistration) ||
277 interest.getName().get(CONTROL_COMMAND_NAME_OFFSET).toString().equals("register")) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800278 return;
279 }
280
281 ControlParameters params = new ControlParameters();
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800282 try {
283 params.wireDecode(interest.getName().get(CONTROL_PARAMETERS_NAME_OFFSET).getValue());
284 params.setFaceId(1);
285 params.setOrigin(0);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800286 params.setCost(0);
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800287 } catch (EncodingException e) {
288 throw new IllegalArgumentException("", e);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800289 }
290
andrewsbrown6d700282016-02-16 18:29:46 -0800291 ControlResponse response = new ControlResponse();
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800292 response.setStatusCode(STATUS_CODE_OK);
andrewsbrown6d700282016-02-16 18:29:46 -0800293 response.setStatusText("OK");
294 response.setBodyAsControlParameters(params);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800295
296 Data data = new Data();
297 data.setName(interest.getName());
andrewsbrown6d700282016-02-16 18:29:46 -0800298 data.setContent(response.wireEncode());
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800299 try {
300 keyChain.sign(data);
Alexander Afanasyev288fcc92018-07-24 17:28:14 -0400301 } catch (SecurityException | KeyChain.Error | TpmBackEnd.Error | PibImpl.Error e) {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800302 LOGGER.log(Level.FINE, "MockKeyChain signing failed", e);
303 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800304
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800305 try {
306 receive(data);
307 } catch (EncodingException e) {
308 LOGGER.log(Level.INFO, "Failed to encode ControlReposnse data", e);
309 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800310 }
311 }
312
313 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700314 * Mock reception of the Interest packet on the Face (from callback).
andrewsbrown8e517fa2016-02-12 15:51:19 -0800315 *
Andrew Brown58191872016-02-05 22:16:57 -0800316 * @param interest the mock-remote interest to add to the PIT
317 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800318 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800319 public void receive(final Interest interest) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800320 transport.receive(interest.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800321 }
322
323 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700324 * Mock reception of the Data packet on the Face (from callback).
andrewsbrown8e517fa2016-02-12 15:51:19 -0800325 *
Andrew Brown58191872016-02-05 22:16:57 -0800326 * @param data the mock-remote data to add to the CS
327 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800328 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800329 public void receive(final Data data) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800330 transport.receive(data.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800331 }
332
Andrew Brown58191872016-02-05 22:16:57 -0800333 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700334 * @return the callback for this face
Andrew Brown58191872016-02-05 22:16:57 -0800335 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800336 public Transport getTransport() {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800337 return transport;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800338 }
Andrew Brown3831baf2015-01-19 13:38:52 -0800339}