blob: 4f73a02dea9650c50a521c2b1821e882c33d8533 [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 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;
28import net.named_data.jndn.transport.Transport;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080029
andrewsbrown1f28bcf2015-04-20 13:29:20 -070030import java.nio.ByteBuffer;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080031import java.util.ArrayList;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080032import java.util.List;
Andrew Brown58191872016-02-05 22:16:57 -080033import java.util.logging.Level;
Andrew Brownec1b0d02015-02-21 13:11:42 -080034import java.util.logging.Logger;
Alexander Afanasyev8e9330f2016-01-25 19:13:40 -080035
Andrew Brown58191872016-02-05 22:16:57 -080036/**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080037 * A client-side face for unit testing.
andrewsbrown8e517fa2016-02-12 15:51:19 -080038 *
39 * @author Alexander Afanasyev, <aa@cs.ucla.edu>
40 * @author Andrew Brown <andrew.brown@intel.com>
Andrew Brown3831baf2015-01-19 13:38:52 -080041 */
Andrew Brown58191872016-02-05 22:16:57 -080042public class MockFace extends Face {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080043 /**
44 * Interests sent out of this MockFace.
45 * <p/>
46 * Sent Interests are appended to this container if options.enablePacketLogger
47 * is true. User of this class is responsible for cleaning up the container,
48 * if necessary. After .expressInterest, .processEvents must be called before
49 * the Interest would show up here.
50 */
51 public final List<Interest> sentInterests = new ArrayList<>();
Andrew Brown58191872016-02-05 22:16:57 -080052
andrewsbrown8e517fa2016-02-12 15:51:19 -080053 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080054 * Data sent out of this MockFace.
55 * <p/>
56 * Sent Data are appended to this container if options.enablePacketLogger is
57 * true. User of this class is responsible for cleaning up the container, if
58 * necessary. After .put, .processEvents must be called before the Data would
59 * show up here.
60 */
61 public final List<Data> sentData = new ArrayList<>();
62
63 /**
64 * Emits whenever an Interest is sent.
65 * <p/>
66 * After .expressInterest, .processEvents must be called before this signal
67 * would be emitted.
68 */
69 public final List<SignalOnSendInterest> onSendInterest = new ArrayList<>();
70
71 /**
72 * Emits whenever a Data packet is sent.
73 * <p/>
74 * After .putData, .processEvents must be called before this signal would be
75 * emitted.
76 */
77 public final List<SignalOnSendData> onSendData = new ArrayList<>();
78
79 private static final Logger LOGGER = Logger.getLogger(MockFace.class.getName());
andrewsbrown62c29122016-05-02 15:58:06 -070080 private MockTransport transport;
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080081 private KeyChain keyChain;
82
83 /////////////////////////////////////////////////////////////////////////////
84
85 /**
86 * API for handling {@link Interest}s.
andrewsbrown8e517fa2016-02-12 15:51:19 -080087 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080088 public interface SignalOnSendInterest {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080089 /**
90 * Callback called when an Interest is sent out through face (towards NFD).
91 * @param interest interest being sent out
92 */
93 void emit(final Interest interest);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080094 }
95
andrewsbrown8e517fa2016-02-12 15:51:19 -080096 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -080097 * API for handling {@link Data}s.
andrewsbrown8e517fa2016-02-12 15:51:19 -080098 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080099 public interface SignalOnSendData {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800100 /**
101 * Callback called when a Data is sent out through face (towards NFD).
102 *
103 * @param data data being sent out
104 */
105 void emit(final Data data);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800106 }
107
108 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800109 * Options for MockFace.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800110 */
Andrew Brown58191872016-02-05 22:16:57 -0800111 public static class Options {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800112 private boolean enablePacketLogging = false;
113 private boolean enableRegistrationReply = false;
114
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800115 /**
116 * @return true if packet logging is enabled
117 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800118 public boolean isEnablePacketLogging() {
119 return enablePacketLogging;
120 }
Andrew Brown58191872016-02-05 22:16:57 -0800121
122 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800123 * Enable/disable packet logging.
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800124 *
125 * @param enablePacketLogging If true, packets sent out of MockFace will be appended to a container
126 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800127 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800128 public Options setEnablePacketLogging(final boolean enablePacketLogging) {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800129 this.enablePacketLogging = enablePacketLogging;
130 return this;
131 }
132
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800133 /**
134 * @return true if prefix registration mocking is enabled
135 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800136 public boolean isEnableRegistrationReply() {
137 return enableRegistrationReply;
138 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800139
140 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800141 * Enable/disable prefix registration mocking.
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800142 *
143 * @param enableRegistrationReply If true, prefix registration command will be automatically replied with a
144 * successful response
145 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800146 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800147 public Options setEnableRegistrationReply(final boolean enableRegistrationReply) {
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800148 this.enableRegistrationReply = enableRegistrationReply;
149 return this;
150 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800151 }
152
andrewsbrown8e517fa2016-02-12 15:51:19 -0800153 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800154 * Default options.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800155 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800156 public static final Options DEFAULT_OPTIONS = new Options()
157 .setEnablePacketLogging(true)
158 .setEnableRegistrationReply(true);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800159
160 /**
andrewsbrown8e517fa2016-02-12 15:51:19 -0800161 * Create MockFace that logs packets in {@link #sentInterests} and
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800162 * {@link #sentData} and emulates NFD prefix registration.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800163 *
164 * @throws SecurityException should not be thrown by this test class
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800165 */
166 public MockFace() throws SecurityException {
167 this(DEFAULT_OPTIONS);
168 }
169
170 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800171 * Create MockFace with the specified options.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800172 * <p>
173 * To create Face that does not log packets:
174 * <pre>
175 * new MockFace(new Options());
176 * // use onSendInterest.add(handler) and onSendData.add(handler)
177 * // to add custom logic when Interest or Data packet are sent
andrewsbrown62c29122016-05-02 15:58:06 -0700178 * // from the upper level (to callback)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800179 * </pre>
180 *
181 * To create Face that just logs packets in sentInterests and sentData:
182 * <pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800183 * new MockFace(new Options(){ enablePacketLogging = true; });
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800184 * </pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800185 *
186 * @param options see {@link Options}
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800187 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800188 public MockFace(final Options options) {
andrewsbrown62c29122016-05-02 15:58:06 -0700189 super(new MockTransport(), null);
190 transport = (MockTransport) node_.getTransport();
andrewsbrown8e517fa2016-02-12 15:51:19 -0800191 transport.setOnSendBlock(new OnIncomingPacket());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800192
andrewsbrown8e517fa2016-02-12 15:51:19 -0800193 try {
194 keyChain = MockKeyChain.configure(new Name("/mock/key"));
195 setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
196 } catch (SecurityException ex) {
197 LOGGER.log(Level.SEVERE, "Unexpected error in MockKeyChain; this class should never throw", ex);
198 throw new Error(ex);
199 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800200
Alexander Afanasyev4c3de792016-02-19 20:18:41 -0800201 if (options.isEnablePacketLogging()) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800202 onSendInterest.add(new SignalOnSendInterest() {
203 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800204 public void emit(final Interest interest) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800205 sentInterests.add(interest);
206 }
207 });
208
209 onSendData.add(new SignalOnSendData() {
210 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800211 public void emit(final Data data) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800212 sentData.add(data);
213 }
214 });
215 }
216
Alexander Afanasyev4c3de792016-02-19 20:18:41 -0800217 if (options.isEnableRegistrationReply()) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800218 onSendInterest.add(new OnPrefixRegistration());
219 }
220 }
221
222 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800223 * Route incoming packets to the correct callbacks.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800224 */
andrewsbrown62c29122016-05-02 15:58:06 -0700225 private class OnIncomingPacket implements MockTransport.OnSendBlockSignal {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800226 /**
227 * {@inheritDoc}
228 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800229 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800230 public void emit(final ByteBuffer buffer) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800231 // @todo Implement NDNLP processing
232
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800233 try {
234 if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
235 TlvDecoder decoder = new TlvDecoder(buffer);
236 if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
237 Interest interest = new Interest();
238 interest.wireDecode(buffer, TlvWireFormat.get());
andrewsbrown8e517fa2016-02-12 15:51:19 -0800239
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800240 for (SignalOnSendInterest signal : onSendInterest) {
241 signal.emit(interest);
242 }
243 } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
244 Data data = new Data();
245 data.wireDecode(buffer, TlvWireFormat.get());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800246
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800247 for (SignalOnSendData signal : onSendData) {
248 signal.emit(data);
249 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800250 }
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800251 } else {
252 LOGGER.info("Received an unknown packet");
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800253 }
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800254 } catch (EncodingException e) {
andrewsbrown62c29122016-05-02 15:58:06 -0700255 LOGGER.log(Level.INFO, "Failed to decodeParameters incoming packet", e);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800256 }
257 }
258 }
259
260 /**
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800261 * Handle prefix registration requests.
andrewsbrown8e517fa2016-02-12 15:51:19 -0800262 */
263 private class OnPrefixRegistration implements SignalOnSendInterest {
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800264 private static final int STATUS_CODE_OK = 200;
265 private static final int CONTROL_PARAMETERS_NAME_OFFSET = -5;
266 private static final int CONTROL_COMMAND_NAME_OFFSET = 3;
andrewsbrown8e517fa2016-02-12 15:51:19 -0800267
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800268 /**
269 * {@inheritDoc}
270 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800271 @Override
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800272 public void emit(final Interest interest) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800273 final Name localhostRegistration = new Name("/localhost/nfd/rib");
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800274 if (!interest.getName().getPrefix(localhostRegistration.size()).equals(localhostRegistration) ||
275 interest.getName().get(CONTROL_COMMAND_NAME_OFFSET).toString().equals("register")) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800276 return;
277 }
278
279 ControlParameters params = new ControlParameters();
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800280 try {
281 params.wireDecode(interest.getName().get(CONTROL_PARAMETERS_NAME_OFFSET).getValue());
282 params.setFaceId(1);
283 params.setOrigin(0);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800284 params.setCost(0);
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800285 } catch (EncodingException e) {
286 throw new IllegalArgumentException("", e);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800287 }
288
andrewsbrown6d700282016-02-16 18:29:46 -0800289 ControlResponse response = new ControlResponse();
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800290 response.setStatusCode(STATUS_CODE_OK);
andrewsbrown6d700282016-02-16 18:29:46 -0800291 response.setStatusText("OK");
292 response.setBodyAsControlParameters(params);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800293
294 Data data = new Data();
295 data.setName(interest.getName());
andrewsbrown6d700282016-02-16 18:29:46 -0800296 data.setContent(response.wireEncode());
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800297 try {
298 keyChain.sign(data);
299 } catch (SecurityException e) {
300 LOGGER.log(Level.FINE, "MockKeyChain signing failed", e);
301 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800302
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800303 try {
304 receive(data);
305 } catch (EncodingException e) {
306 LOGGER.log(Level.INFO, "Failed to encode ControlReposnse data", e);
307 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800308 }
309 }
310
311 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700312 * Mock reception of the Interest packet on the Face (from callback).
andrewsbrown8e517fa2016-02-12 15:51:19 -0800313 *
Andrew Brown58191872016-02-05 22:16:57 -0800314 * @param interest the mock-remote interest to add to the PIT
315 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800316 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800317 public void receive(final Interest interest) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800318 transport.receive(interest.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800319 }
320
321 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700322 * Mock reception of the Data packet on the Face (from callback).
andrewsbrown8e517fa2016-02-12 15:51:19 -0800323 *
Andrew Brown58191872016-02-05 22:16:57 -0800324 * @param data the mock-remote data to add to the CS
325 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800326 */
Alexander Afanasyevcbc41012016-02-19 20:10:57 -0800327 public void receive(final Data data) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800328 transport.receive(data.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800329 }
330
Andrew Brown58191872016-02-05 22:16:57 -0800331 /**
andrewsbrown62c29122016-05-02 15:58:06 -0700332 * @return the callback for this face
Andrew Brown58191872016-02-05 22:16:57 -0800333 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800334 public Transport getTransport() {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800335 return transport;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800336 }
Andrew Brown3831baf2015-01-19 13:38:52 -0800337}