blob: f47959c6a85431dd08a38564479c120c4ff9a920 [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/**
37 * 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 {
43
andrewsbrown8e517fa2016-02-12 15:51:19 -080044 /**
45 * API for handling {@link Interest}s
46 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080047 public interface SignalOnSendInterest {
48 void emit(Interest interest) throws EncodingException, SecurityException;
49 }
50
andrewsbrown8e517fa2016-02-12 15:51:19 -080051 /**
52 * API for handling {@link Data}s
53 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080054 public interface SignalOnSendData {
55 void emit(Data data);
56 }
57
58 /**
59 * Options for MockFace
60 */
Andrew Brown58191872016-02-05 22:16:57 -080061 public static class Options {
Alexander Afanasyev522327e2016-02-19 19:40:31 -080062 private boolean enablePacketLogging = false;
63 private boolean enableRegistrationReply = false;
64
65 public boolean isEnablePacketLogging() {
66 return enablePacketLogging;
67 }
Andrew Brown58191872016-02-05 22:16:57 -080068
69 /**
Alexander Afanasyev522327e2016-02-19 19:40:31 -080070 * Enable/disable packet logging
71 *
72 * @param enablePacketLogging If true, packets sent out of MockFace will be appended to a container
73 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080074 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -080075 public Options setEnablePacketLogging(boolean enablePacketLogging) {
76 this.enablePacketLogging = enablePacketLogging;
77 return this;
78 }
79
80 public boolean isEnableRegistrationReply() {
81 return enableRegistrationReply;
82 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080083
84 /**
Alexander Afanasyev522327e2016-02-19 19:40:31 -080085 * Enable/disable prefix registration mocking
86 *
87 * @param enableRegistrationReply If true, prefix registration command will be automatically replied with a
88 * successful response
89 * @return this
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080090 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -080091 public Options setEnableRegistrationReply(boolean enableRegistrationReply) {
92 this.enableRegistrationReply = enableRegistrationReply;
93 return this;
94 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080095 }
96
andrewsbrown8e517fa2016-02-12 15:51:19 -080097 /**
98 * Default options
99 */
Alexander Afanasyev522327e2016-02-19 19:40:31 -0800100 public static final Options DEFAULT_OPTIONS = new Options()
101 .setEnablePacketLogging(true)
102 .setEnableRegistrationReply(true);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800103
104 /**
andrewsbrown8e517fa2016-02-12 15:51:19 -0800105 * Create MockFace that logs packets in {@link #sentInterests} and
106 * {@link #sentData} and emulates NFD prefix registration
107 *
108 * @throws SecurityException should not be thrown by this test class
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800109 */
110 public MockFace() throws SecurityException {
111 this(DEFAULT_OPTIONS);
112 }
113
114 /**
115 * Create MockFace with the specified options
116 * <p>
117 * To create Face that does not log packets:
118 * <pre>
119 * new MockFace(new Options());
120 * // use onSendInterest.add(handler) and onSendData.add(handler)
121 * // to add custom logic when Interest or Data packet are sent
122 * // from the upper level (to transport)
123 * </pre>
124 *
125 * To create Face that just logs packets in sentInterests and sentData:
126 * <pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800127 * new MockFace(new Options(){ enablePacketLogging = true; });
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800128 * </pre>
andrewsbrown8e517fa2016-02-12 15:51:19 -0800129 *
130 * @param options see {@link Options}
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800131 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800132 public MockFace(Options options) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800133 super(new MockFaceTransport(), null);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800134 transport = (MockFaceTransport) node_.getTransport();
135 transport.setOnSendBlock(new OnIncomingPacket());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800136
andrewsbrown8e517fa2016-02-12 15:51:19 -0800137 try {
138 keyChain = MockKeyChain.configure(new Name("/mock/key"));
139 setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
140 } catch (SecurityException ex) {
141 LOGGER.log(Level.SEVERE, "Unexpected error in MockKeyChain; this class should never throw", ex);
142 throw new Error(ex);
143 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800144
145 if (options.enablePacketLogging) {
146 onSendInterest.add(new SignalOnSendInterest() {
147 @Override
148 public void emit(Interest interest) {
149 sentInterests.add(interest);
150 }
151 });
152
153 onSendData.add(new SignalOnSendData() {
154 @Override
155 public void emit(Data data) {
156 sentData.add(data);
157 }
158 });
159 }
160
161 if (options.enableRegistrationReply) {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800162 onSendInterest.add(new OnPrefixRegistration());
163 }
164 }
165
166 /**
167 * Route incoming packets to the correct callbacks
168 */
169 private class OnIncomingPacket implements MockFaceTransport.OnSendBlockSignal {
170
171 @Override
172 public void emit(ByteBuffer buffer) throws EncodingException, SecurityException {
173 // @todo Implement NDNLP processing
174
175 if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
176 TlvDecoder decoder = new TlvDecoder(buffer);
177 if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
178 Interest interest = new Interest();
179 interest.wireDecode(buffer, TlvWireFormat.get());
180
181 for (SignalOnSendInterest signal : onSendInterest) {
182 signal.emit(interest);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800183 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800184 } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800185 Data data = new Data();
andrewsbrown8e517fa2016-02-12 15:51:19 -0800186 data.wireDecode(buffer, TlvWireFormat.get());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800187
andrewsbrown8e517fa2016-02-12 15:51:19 -0800188 for (SignalOnSendData signal : onSendData) {
189 signal.emit(data);
190 }
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800191 }
andrewsbrown8e517fa2016-02-12 15:51:19 -0800192 } else {
193 LOGGER.info("Received an unknown packet");
194 }
195 }
196 }
197
198 /**
199 * Handle prefix registration requests
200 */
201 private class OnPrefixRegistration implements SignalOnSendInterest {
202
203 @Override
204 public void emit(Interest interest) throws EncodingException, SecurityException {
205 final Name localhostRegistration = new Name("/localhost/nfd/rib");
206 if (!interest.getName().getPrefix(3).equals(localhostRegistration)) {
207 return;
208 }
209
210 ControlParameters params = new ControlParameters();
211 params.wireDecode(interest.getName().get(-5).getValue());
212 params.setFaceId(1);
213 params.setOrigin(0);
214
215 if ("register".equals(interest.getName().get(3).toString())) {
216 params.setCost(0);
217 }
218
andrewsbrown6d700282016-02-16 18:29:46 -0800219 ControlResponse response = new ControlResponse();
220 response.setStatusCode(200);
221 response.setStatusText("OK");
222 response.setBodyAsControlParameters(params);
andrewsbrown8e517fa2016-02-12 15:51:19 -0800223
224 Data data = new Data();
225 data.setName(interest.getName());
andrewsbrown6d700282016-02-16 18:29:46 -0800226 data.setContent(response.wireEncode());
andrewsbrown8e517fa2016-02-12 15:51:19 -0800227 keyChain.sign(data);
228
229 receive(data);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800230 }
231 }
232
233 /**
234 * Mock reception of the Interest packet on the Face (from transport)
andrewsbrown8e517fa2016-02-12 15:51:19 -0800235 *
Andrew Brown58191872016-02-05 22:16:57 -0800236 * @param interest the mock-remote interest to add to the PIT
237 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800238 */
239 public void receive(Interest interest) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800240 transport.receive(interest.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800241 }
242
243 /**
244 * Mock reception of the Data packet on the Face (from transport)
andrewsbrown8e517fa2016-02-12 15:51:19 -0800245 *
Andrew Brown58191872016-02-05 22:16:57 -0800246 * @param data the mock-remote data to add to the CS
247 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800248 */
249 public void receive(Data data) throws EncodingException {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800250 transport.receive(data.wireEncode().buf());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800251 }
252
Andrew Brown58191872016-02-05 22:16:57 -0800253 /**
254 * @return the transport for this face
255 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800256 public Transport getTransport() {
andrewsbrown8e517fa2016-02-12 15:51:19 -0800257 return transport;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800258 }
259
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800260 /**
261 * Interests sent out of this MockFace
262 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800263 * Sent Interests are appended to this container if options.enablePacketLogger
264 * is true. User of this class is responsible for cleaning up the container,
265 * if necessary. After .expressInterest, .processEvents must be called before
266 * the Interest would show up here.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800267 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800268 public final List<Interest> sentInterests = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800269
270 /**
271 * Data sent out of this MockFace
272 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800273 * Sent Data are appended to this container if options.enablePacketLogger is
274 * true. User of this class is responsible for cleaning up the container, if
275 * necessary. After .put, .processEvents must be called before the Data would
276 * show up here.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800277 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800278 public final List<Data> sentData = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800279
280 /**
281 * Emits whenever an Interest is sent
282 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800283 * After .expressInterest, .processEvents must be called before this signal
284 * would be emitted.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800285 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800286 public final List<SignalOnSendInterest> onSendInterest = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800287
288 /**
289 * Emits whenever a Data packet is sent
290 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800291 * After .putData, .processEvents must be called before this signal would be
292 * emitted.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800293 */
andrewsbrown8e517fa2016-02-12 15:51:19 -0800294 public final List<SignalOnSendData> onSendData = new ArrayList<>();
Andrew Brown3831baf2015-01-19 13:38:52 -0800295
andrewsbrown8e517fa2016-02-12 15:51:19 -0800296 private static final Logger LOGGER = Logger.getLogger(MockFace.class.getName());
297 private MockFaceTransport transport;
298 private KeyChain keyChain;
Andrew Brown3831baf2015-01-19 13:38:52 -0800299}