diff --git a/californium-core/src/main/java/org/eclipse/californium/core/network/CoapEndpoint.java b/californium-core/src/main/java/org/eclipse/californium/core/network/CoapEndpoint.java index 682cd1bf29..f235322c4e 100644 --- a/californium-core/src/main/java/org/eclipse/californium/core/network/CoapEndpoint.java +++ b/californium-core/src/main/java/org/eclipse/californium/core/network/CoapEndpoint.java @@ -26,6 +26,8 @@ * Bosch Software Innovations GmbH - adapt message parsing error handling * Joe Magerramov (Amazon Web Services) - CoAP over TCP support. * Bosch Software Innovations GmbH - adjust request scheme for TCP + * Achim Kraus (Bosch Software Innovations GmbH) - introduce CorrelationStrategy + * (fix GitHub issue #104) ******************************************************************************/ package org.eclipse.californium.core.network; @@ -69,6 +71,7 @@ import org.eclipse.californium.core.server.MessageDeliverer; import org.eclipse.californium.elements.Connector; import org.eclipse.californium.elements.CorrelationContext; +import org.eclipse.californium.elements.CorrelationStrategy; import org.eclipse.californium.elements.MessageCallback; import org.eclipse.californium.elements.RawData; import org.eclipse.californium.elements.RawDataChannel; @@ -142,7 +145,7 @@ public class CoapEndpoint implements Endpoint { /** The connector over which the endpoint connects to the network */ private final Connector connector; - + private final String scheme; private final String secureScheme; @@ -292,17 +295,20 @@ public CoapEndpoint(Connector connector, NetworkConfig config, ObservationStore this.connector.setRawDataReceiver(new InboxImpl()); ObservationStore observationStore = store != null ? store : new InMemoryObservationStore(); this.exchangeStore = exchangeStore; - + CorrelationStrategy strategy = CorrelationStrategyFactory.create(config); + LOGGER.log(Level.CONFIG, "{0} uses {1}", + new Object[]{getClass().getSimpleName(), strategy.getName()}); + if (connector.isSchemeSupported(CoAP.COAP_TCP_URI_SCHEME) || connector.isSchemeSupported(CoAP.COAP_SECURE_TCP_URI_SCHEME)) { - this.matcher = new TcpMatcher(config, new NotificationDispatcher(), observationStore); + this.matcher = new TcpMatcher(config, new NotificationDispatcher(), observationStore, strategy); this.coapstack = new CoapTcpStack(config, new OutboxImpl()); this.serializer = new TcpDataSerializer(); this.parser = new TcpDataParser(); this.scheme = CoAP.COAP_TCP_URI_SCHEME; this.secureScheme = CoAP.COAP_SECURE_TCP_URI_SCHEME; } else { - this.matcher = new UdpMatcher(config, new NotificationDispatcher(), observationStore); + this.matcher = new UdpMatcher(config, new NotificationDispatcher(), observationStore, strategy); this.coapstack = new CoapUdpStack(config, new OutboxImpl()); this.serializer = new UdpDataSerializer(); this.parser = new UdpDataParser(); diff --git a/californium-core/src/main/java/org/eclipse/californium/core/network/CorrelationStrategyFactory.java b/californium-core/src/main/java/org/eclipse/californium/core/network/CorrelationStrategyFactory.java new file mode 100644 index 0000000000..30b7096e28 --- /dev/null +++ b/californium-core/src/main/java/org/eclipse/californium/core/network/CorrelationStrategyFactory.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2017 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.html. + * + * Contributors: + * Bosch Software Innovations GmbH - introduce CorrelationStrategy + * (fix GitHub issue #104) + ******************************************************************************/ +package org.eclipse.californium.core.network; + +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.elements.CorrelationStrategy; +import org.eclipse.californium.elements.RelaxedCorrelationStrategy; +import org.eclipse.californium.elements.StrictlyCorrelationStrategy; + +/** + * Factory for correlation strategy. + */ +public abstract class CorrelationStrategyFactory { + + /** + * Factory instance for correlation strategy. Default creates correlation + * strategy according the configuration. If USE_STRICT_RESPONSE_MATCHING is + * set, use {@link StrictlyCorrelationStrategy}, otherwise + * {@link RelaxedCorrelationStrategy}. + */ + private static CorrelationStrategyFactory factory = new CorrelationStrategyFactory() { + + protected CorrelationStrategy createStrategy(NetworkConfig config) { + return config.getBoolean(NetworkConfig.Keys.USE_STRICT_RESPONSE_MATCHING) ? new StrictlyCorrelationStrategy() + : new RelaxedCorrelationStrategy(); + } + }; + + /** + * Create correlation strategy based on configuration. + * + * @param config configuration + * @return correlation strategy + */ + protected abstract CorrelationStrategy createStrategy(NetworkConfig config); + + /** + * Replace the strategy factory with a custom factory. + * + * @param newFactory new correlation strategy factory. If null, the current + * factory is not replaced and just the old one returned. + * @return old strategy factory + */ + public static synchronized CorrelationStrategyFactory replaceFactory(CorrelationStrategyFactory newFactory) { + CorrelationStrategyFactory oldFactory = factory; + if (null != newFactory) { + factory = newFactory; + } + return oldFactory; + } + + /** + * Get the current factory. + * + * @return strategy factory + */ + private static synchronized CorrelationStrategyFactory getFactory() { + return factory; + } + + /** + * Create correlation strategy according the configuration. + * + * @param config configuration. + * @return correlation strategy + */ + public static CorrelationStrategy create(NetworkConfig config) { + return getFactory().createStrategy(config); + } +} diff --git a/californium-core/src/main/java/org/eclipse/californium/core/network/TcpMatcher.java b/californium-core/src/main/java/org/eclipse/californium/core/network/TcpMatcher.java index 20aa976416..a373f5b63f 100644 --- a/californium-core/src/main/java/org/eclipse/californium/core/network/TcpMatcher.java +++ b/californium-core/src/main/java/org/eclipse/californium/core/network/TcpMatcher.java @@ -22,6 +22,9 @@ * of Response(s) to Request (fix GitHub issue #1) * Joe Magerramov (Amazon Web Services) - CoAP over TCP support. * Achim Kraus (Bosch Software Innovations GmbH) - processing of notifies according UdpMatcher. + * Achim Kraus (Bosch Software Innovations GmbH) - replace isResponseRelatedToRequest + * with CorrelationStrategy + * (fix GitHub issue #104) ******************************************************************************/ package org.eclipse.californium.core.network; @@ -36,6 +39,7 @@ import org.eclipse.californium.core.observe.NotificationListener; import org.eclipse.californium.core.observe.ObservationStore; import org.eclipse.californium.elements.CorrelationContext; +import org.eclipse.californium.elements.CorrelationStrategy; /** * Matcher that runs over reliable TCP/TLS protocol. Based on @@ -45,6 +49,7 @@ public final class TcpMatcher extends BaseMatcher { private static final Logger LOGGER = Logger.getLogger(TcpMatcher.class.getName()); private final ExchangeObserver exchangeObserver = new ExchangeObserverImpl(); + private final CorrelationStrategy matchingStrategy; /** * Creates a new matcher for running CoAP over TCP. @@ -54,12 +59,14 @@ public final class TcpMatcher extends BaseMatcher { * received from peers. * @param observationStore the object to use for keeping track of * observations created by the endpoint this matcher is part of. + * @param matchingStrategy strategy to match the correlation context of request and response * @throws NullPointerException if the configuration, notification listener, * or the observation store is {@code null}. */ public TcpMatcher(final NetworkConfig config, final NotificationListener notificationListener, - final ObservationStore observationStore) { + final ObservationStore observationStore, final CorrelationStrategy matchingStrategy) { super(config, notificationListener, observationStore); + this.matchingStrategy = matchingStrategy; } @Override @@ -124,7 +131,7 @@ public Exchange receiveResponse(final Response response, final CorrelationContex if (exchange == null) { // There is no exchange with the given token - ignore response return null; - } else if (isResponseRelatedToRequest(exchange, responseContext)) { + } else if (matchingStrategy.isResponseRelatedToRequest(exchange.getCorrelationContext(), responseContext)) { return exchange; } else { LOGGER.log(Level.INFO, @@ -134,10 +141,6 @@ public Exchange receiveResponse(final Response response, final CorrelationContex } } - private static boolean isResponseRelatedToRequest(final Exchange exchange, final CorrelationContext responseContext) { - return exchange.getCorrelationContext() == null || exchange.getCorrelationContext().equals(responseContext); - } - @Override public Exchange receiveEmptyMessage(final EmptyMessage message) { /* ignore received empty messages via tcp */ diff --git a/californium-core/src/main/java/org/eclipse/californium/core/network/UdpMatcher.java b/californium-core/src/main/java/org/eclipse/californium/core/network/UdpMatcher.java index a28432e854..6196a9a9e7 100644 --- a/californium-core/src/main/java/org/eclipse/californium/core/network/UdpMatcher.java +++ b/californium-core/src/main/java/org/eclipse/californium/core/network/UdpMatcher.java @@ -20,6 +20,9 @@ * explicit String concatenation * Bosch Software Innovations GmbH - use correlation context to improve matching * of Response(s) to Request (fix GitHub issue #1) + * Achim Kraus (Bosch Software Innovations GmbH) - replace isResponseRelatedToRequest + * with CorrelationStrategy + * (fix GitHub issue #104) ******************************************************************************/ package org.eclipse.californium.core.network; @@ -39,7 +42,7 @@ import org.eclipse.californium.core.observe.ObservationStore; import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.elements.CorrelationContext; -import org.eclipse.californium.elements.DtlsCorrelationContext; +import org.eclipse.californium.elements.CorrelationStrategy; /** * A Matcher for CoAP messages transmitted over UDP. @@ -50,7 +53,7 @@ public final class UdpMatcher extends BaseMatcher { private final ExchangeObserver exchangeObserver = new ExchangeObserverImpl(); // TODO: Multicast Exchanges: should not be removed from deduplicator - private final boolean useStrictResponseMatching; + private final CorrelationStrategy matchingStrategy; /** * Creates a new matcher for running CoAP over UDP. @@ -59,18 +62,14 @@ public final class UdpMatcher extends BaseMatcher { * @param notificationListener the callback to invoke for notifications received from peers. * @param observationStore the object to use for keeping track of observations created by the endpoint * this matcher is part of. + * @param matchingStrategy strategy to match the correlation context of request and response * @throws NullPointerException if the configuration, notification listener, * or the observation store is {@code null}. */ public UdpMatcher(final NetworkConfig config, final NotificationListener notificationListener, - final ObservationStore observationStore) { + final ObservationStore observationStore, final CorrelationStrategy matchingStrategy) { super(config, notificationListener, observationStore); - useStrictResponseMatching = config.getBoolean(NetworkConfig.Keys.USE_STRICT_RESPONSE_MATCHING); - - LOGGER.log(Level.CONFIG, "{0} uses {1}={2}", - new Object[]{getClass().getSimpleName(), - NetworkConfig.Keys.USE_STRICT_RESPONSE_MATCHING, - useStrictResponseMatching}); + this.matchingStrategy = matchingStrategy; } @Override @@ -216,7 +215,7 @@ public Exchange receiveResponse(final Response response, final CorrelationContex } // ignore response return null; - } else if (isResponseRelatedToRequest(exchange, responseContext)) { + } else if (matchingStrategy.isResponseRelatedToRequest(exchange.getCorrelationContext(), responseContext)) { // we have received a Response matching the token of an ongoing Exchange's Request // according to the CoAP spec (https://tools.ietf.org/html/rfc7252#section-4.5), @@ -248,50 +247,6 @@ public Exchange receiveResponse(final Response response, final CorrelationContex } } - private boolean isResponseRelatedToRequest(final Exchange exchange, final CorrelationContext responseContext) { - if (exchange.getCorrelationContext() == null) { - // no correlation information available for request, thus any - // additional correlation information available in the response is ignored - return true; - } else if (exchange.getCorrelationContext().get(DtlsCorrelationContext.KEY_SESSION_ID) != null) { - // original request has been sent via a DTLS protected transport - // check if the response has been received in the same DTLS session - if (useStrictResponseMatching) { - return isResponseStrictlyRelatedToDtlsRequest(exchange.getCorrelationContext(), responseContext); - } else { - return isResponseRelatedToDtlsRequest(exchange.getCorrelationContext(), responseContext); - } - } else { - // compare message context used for sending original request to context - // the response has been received in - return exchange.getCorrelationContext().equals(responseContext); - } - } - - private boolean isResponseRelatedToDtlsRequest(final CorrelationContext requestContext, final CorrelationContext responseContext) { - if (responseContext == null) { - return false; - } else { - return requestContext.get(DtlsCorrelationContext.KEY_SESSION_ID) - .equals(responseContext.get(DtlsCorrelationContext.KEY_SESSION_ID)) - && requestContext.get(DtlsCorrelationContext.KEY_CIPHER) - .equals(responseContext.get(DtlsCorrelationContext.KEY_CIPHER)); - } - } - - private boolean isResponseStrictlyRelatedToDtlsRequest(final CorrelationContext requestContext, final CorrelationContext responseContext) { - if (responseContext == null) { - return false; - } else { - return requestContext.get(DtlsCorrelationContext.KEY_SESSION_ID) - .equals(responseContext.get(DtlsCorrelationContext.KEY_SESSION_ID)) - && requestContext.get(DtlsCorrelationContext.KEY_EPOCH) - .equals(responseContext.get(DtlsCorrelationContext.KEY_EPOCH)) - && requestContext.get(DtlsCorrelationContext.KEY_CIPHER) - .equals(responseContext.get(DtlsCorrelationContext.KEY_CIPHER)); - } - } - @Override public Exchange receiveEmptyMessage(final EmptyMessage message) { @@ -366,7 +321,6 @@ public void completed(final Exchange exchange) { // this endpoint created the Exchange to respond to a request Response response = exchange.getCurrentResponse(); - Request request = exchange.getCurrentRequest(); if (response != null && response.getType() != Type.ACK) { // this means that we have sent the response in a separate CON/NON message diff --git a/californium-core/src/test/java/org/eclipse/californium/core/network/TcpMatcherTest.java b/californium-core/src/test/java/org/eclipse/californium/core/network/TcpMatcherTest.java index 21f8abdaf5..964266d1e8 100644 --- a/californium-core/src/test/java/org/eclipse/californium/core/network/TcpMatcherTest.java +++ b/californium-core/src/test/java/org/eclipse/californium/core/network/TcpMatcherTest.java @@ -12,6 +12,8 @@ * * Contributors: * Bosch Software Innovations GmbH - initial creation + * Achim Kraus (Bosch Software Innovations GmbH) - add CorrelationStrategy + * (fix GitHub issue #104) ******************************************************************************/ package org.eclipse.californium.core.network; @@ -56,8 +58,7 @@ public void onNotification(Request request, Response response) { } }; - - TcpMatcher matcher = new TcpMatcher(config, notificationListener, new InMemoryObservationStore()); + TcpMatcher matcher = new TcpMatcher(config, notificationListener, new InMemoryObservationStore(), CorrelationStrategyFactory.create(config)); matcher.start(); return matcher; } diff --git a/californium-core/src/test/java/org/eclipse/californium/core/network/UdpMatcherTest.java b/californium-core/src/test/java/org/eclipse/californium/core/network/UdpMatcherTest.java index d8904dfa2a..eac04a7aa4 100644 --- a/californium-core/src/test/java/org/eclipse/californium/core/network/UdpMatcherTest.java +++ b/californium-core/src/test/java/org/eclipse/californium/core/network/UdpMatcherTest.java @@ -12,6 +12,8 @@ * * Contributors: * Bosch Software Innovations GmbH - initial creation + * Achim Kraus (Bosch Software Innovations GmbH) - add CorrelationStrategy + * (fix GitHub issue #104) ******************************************************************************/ package org.eclipse.californium.core.network; @@ -255,7 +257,7 @@ public void onNotification(Request request, Response response) { }; - UdpMatcher matcher = new UdpMatcher(config, notificationListener, observationStore); + UdpMatcher matcher = new UdpMatcher(config, notificationListener, observationStore, CorrelationStrategyFactory.create(config)); matcher.setMessageExchangeStore(messageExchangeStore); matcher.start(); diff --git a/element-connector/src/main/java/org/eclipse/californium/elements/CorrelationStrategy.java b/element-connector/src/main/java/org/eclipse/californium/elements/CorrelationStrategy.java new file mode 100644 index 0000000000..54f9391739 --- /dev/null +++ b/element-connector/src/main/java/org/eclipse/californium/elements/CorrelationStrategy.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2017 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.html. + * + * Contributors: + * Bosch Software Innovations GmbH - add flexible correlation context matching + * (fix GitHub issue #104) + ******************************************************************************/ +package org.eclipse.californium.elements; + +/** + * Interface for correlation context processing. Enable implementor to flexible + * decide on context correlation information. + */ +public interface CorrelationStrategy { + + /** + * Return strategy name. Used for logging. + * + * @return name of strategy. + */ + String getName(); + + /** + * Check, if responses is related to the request. + * + * @param requestContext correlation context of request + * @param responseContext correlation context of response + * @return true, if response is related to the request, false, if response + * should not be considered for this request. + */ + boolean isResponseRelatedToRequest(CorrelationContext requestContext, CorrelationContext responseContext); + +} diff --git a/element-connector/src/main/java/org/eclipse/californium/elements/RelaxedCorrelationStrategy.java b/element-connector/src/main/java/org/eclipse/californium/elements/RelaxedCorrelationStrategy.java new file mode 100644 index 0000000000..820abe9af1 --- /dev/null +++ b/element-connector/src/main/java/org/eclipse/californium/elements/RelaxedCorrelationStrategy.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2017 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.html. + * + * Contributors: + * Bosch Software Innovations GmbH - add flexible correlation context matching + * (fix GitHub issue #104) + ******************************************************************************/ +package org.eclipse.californium.elements; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default correlation strategy. Matches DTLS without epoch. + */ +public class RelaxedCorrelationStrategy implements CorrelationStrategy { + + private static final Logger LOGGER = Logger.getLogger(RelaxedCorrelationStrategy.class.getName()); + + @Override + public String getName() { + return "relaxed correlation"; + } + + @Override + public boolean isResponseRelatedToRequest(CorrelationContext requestContext, CorrelationContext responseContext) { + return internalMatch(requestContext, responseContext); + } + + private final boolean internalMatch(CorrelationContext requestedContext, CorrelationContext availableContext) { + if (null == requestedContext) { + return true; + } else if (null == availableContext) { + return false; + } + if (requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID) != null) { + boolean match = requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID).equals( + availableContext.get(DtlsCorrelationContext.KEY_SESSION_ID)) + && requestedContext.get(DtlsCorrelationContext.KEY_CIPHER).equals( + availableContext.get(DtlsCorrelationContext.KEY_CIPHER)); + + LOGGER.log( + match ? Level.FINEST : Level.WARNING, + "(D)TLS session {0}, {1}", + new Object[] { requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID), + availableContext.get(DtlsCorrelationContext.KEY_SESSION_ID) }); + LOGGER.log( + match ? Level.FINEST : Level.WARNING, + "(D)TLS cipher {0}, {1}", + new Object[] { requestedContext.get(DtlsCorrelationContext.KEY_CIPHER), + availableContext.get(DtlsCorrelationContext.KEY_CIPHER) }); + return match; + } + return requestedContext.equals(availableContext); + } + +} diff --git a/element-connector/src/main/java/org/eclipse/californium/elements/StrictlyCorrelationStrategy.java b/element-connector/src/main/java/org/eclipse/californium/elements/StrictlyCorrelationStrategy.java new file mode 100644 index 0000000000..42aaf9bd10 --- /dev/null +++ b/element-connector/src/main/java/org/eclipse/californium/elements/StrictlyCorrelationStrategy.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2017 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.html. + * + * Contributors: + * Bosch Software Innovations GmbH - add flexible correlation context matching + * (fix GitHub issue #104) + ******************************************************************************/ +package org.eclipse.californium.elements; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default correlation strategy. Uses strictly matching for DTLS including the security + * epoch. + */ +public class StrictlyCorrelationStrategy implements CorrelationStrategy { + + private static final Logger LOGGER = Logger.getLogger(RelaxedCorrelationStrategy.class.getName()); + + @Override + public String getName() { + return "strict correlation"; + } + + @Override + public boolean isResponseRelatedToRequest(CorrelationContext requestContext, CorrelationContext responseContext) { + return internalMatch(requestContext, responseContext); + } + + private final boolean internalMatch(CorrelationContext requestedContext, CorrelationContext availableContext) { + if (null == requestedContext) { + return true; + } else if (null == availableContext) { + return false; + } + if (requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID) != null) { + boolean match = requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID).equals( + availableContext.get(DtlsCorrelationContext.KEY_SESSION_ID)) + && requestedContext.get(DtlsCorrelationContext.KEY_EPOCH).equals( + availableContext.get(DtlsCorrelationContext.KEY_EPOCH)) + && requestedContext.get(DtlsCorrelationContext.KEY_CIPHER).equals( + availableContext.get(DtlsCorrelationContext.KEY_CIPHER)); + + LOGGER.log( + match ? Level.FINEST : Level.WARNING, + "(D)TLS session {0}, {1}", + new Object[] { requestedContext.get(DtlsCorrelationContext.KEY_SESSION_ID), + availableContext.get(DtlsCorrelationContext.KEY_SESSION_ID) }); + LOGGER.log( + match ? Level.FINEST : Level.WARNING, + "(D)TLS epoch {0}, {1}", + new Object[] { requestedContext.get(DtlsCorrelationContext.KEY_EPOCH), + availableContext.get(DtlsCorrelationContext.KEY_EPOCH) }); + LOGGER.log( + match ? Level.FINEST : Level.WARNING, + "(D)TLS cipher {0}, {1}", + new Object[] { requestedContext.get(DtlsCorrelationContext.KEY_CIPHER), + availableContext.get(DtlsCorrelationContext.KEY_CIPHER) }); + return match; + } + return requestedContext.equals(availableContext); + } + +}