Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UnconnectedPeerException and consider HanshakeTimeout as "regular" timeout #812

Merged
merged 4 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@

import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.elements.exception.EndpointUnconnectedException;
import org.eclipse.californium.scandium.dtls.DtlsHandshakeTimeoutException;
import org.eclipse.leshan.core.request.exception.RequestCanceledException;
import org.eclipse.leshan.core.request.exception.RequestRejectedException;
import org.eclipse.leshan.core.request.exception.SendFailedException;
import org.eclipse.leshan.core.request.exception.TimeoutException;
import org.eclipse.leshan.core.request.exception.TimeoutException.Type;
import org.eclipse.leshan.core.request.exception.UnconnectedPeerException;
import org.eclipse.leshan.core.response.ErrorCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -111,8 +116,8 @@ public void onReadyToSend() {
public void onTimeout() {
cancelCleaningTask();
if (eventRaised.compareAndSet(false, true)) {
errorCallback.onError(new org.eclipse.leshan.core.request.exception.TimeoutException("Request %s timed out",
coapRequest.getURI()));
errorCallback.onError(new TimeoutException(Type.COAP_TIMEOUT,
"Request %s timed out : CoAP or blockwise timeout", coapRequest.getURI()));
} else {
LOG.debug("OnTimeout callback ignored because an event was already raised for this request {}",
coapRequest);
Expand All @@ -124,8 +129,8 @@ public void onCancel() {
cancelCleaningTask();
if (eventRaised.compareAndSet(false, true)) {
if (responseTimedOut.get()) {
errorCallback.onError(new org.eclipse.leshan.core.request.exception.TimeoutException(
"Request %s timed out", coapRequest.getURI()));
errorCallback.onError(new TimeoutException(Type.RESPONSE_TIMEOUT,
"Request %s timed out : no response received", coapRequest.getURI()));
} else {
errorCallback.onError(new RequestCanceledException("Request %s cancelled", coapRequest.getURI()));
}
Expand All @@ -150,7 +155,17 @@ public void onReject() {
public void onSendError(Throwable error) {
cancelCleaningTask();
if (eventRaised.compareAndSet(false, true)) {
errorCallback.onError(new SendFailedException(error, "Unable to send request %s", coapRequest.getURI()));
if (error instanceof DtlsHandshakeTimeoutException) {
errorCallback.onError(new TimeoutException(Type.DTLS_HANDSHAKE_TIMEOUT, error,
"Request %s timeout : dtls handshake timeout", coapRequest.getURI()));
} else if (error instanceof EndpointUnconnectedException) {
errorCallback.onError(new UnconnectedPeerException(error,
"Unable to send request %s : peer is not connected (no DTLS connection)",
coapRequest.getURI()));
} else {
errorCallback
.onError(new SendFailedException(error, "Unable to send request %s", coapRequest.getURI()));
}
} else {
LOG.debug("onSendError callback ignored because an event was already raised for this request {}",
coapRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@

import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.scandium.dtls.DtlsHandshakeTimeoutException;
import org.eclipse.californium.elements.exception.EndpointUnconnectedException;
import org.eclipse.leshan.core.request.exception.RequestCanceledException;
import org.eclipse.leshan.core.request.exception.RequestRejectedException;
import org.eclipse.leshan.core.request.exception.SendFailedException;
import org.eclipse.leshan.core.request.exception.UnconnectedPeerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -94,7 +97,14 @@ public void onReject() {

@Override
public void onSendError(Throwable error) {
exception.set(new SendFailedException(error, "Request %s cannot be sent", coapRequest, error.getMessage()));
if (error instanceof DtlsHandshakeTimeoutException) {
coapTimeout.set(true);
} else if (error instanceof EndpointUnconnectedException) {
exception.set(new UnconnectedPeerException(error,
"Unable to send request %s : peer is not connected (no DTLS connection)", coapRequest.getURI()));
} else {
exception.set(new SendFailedException(error, "Request %s cannot be sent", coapRequest, error.getMessage()));
}
latch.countDown();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,35 @@

public class TimeoutException extends Exception {

public enum Type {
RESPONSE_TIMEOUT, COAP_TIMEOUT, DTLS_HANDSHAKE_TIMEOUT
}

private static final long serialVersionUID = -8966041387554358975L;

public TimeoutException(String message, Object... args) {
private Type type;

public TimeoutException(Type type, String message, Object... args) {
super(String.format(message, args));
this.type = type;
}

public TimeoutException(Type type, Throwable cause, String message, Object... args) {
super(String.format(message, args), cause);
this.type = type;
}

/**
* Get the kind of timeout.
* <p>
* See https://github.com/eclipse/leshan/wiki/Request-Timeout for more details.
* <p>
* Current implementation is not able to make differences between CoAP and Blockwise timeout, all is regroup over
* <code>COAP_TIMEOUT</code>.
*
* @return the kind of timeout
*/
public Type getType() {
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2020 Sierra Wireless 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:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.core.request.exception;

public class UnconnectedPeerException extends RuntimeException {

private static final long serialVersionUID = 1L;

public UnconnectedPeerException(String m) {
super(m);
}

public UnconnectedPeerException(String m, Object... args) {
super(String.format(m, args));
}

public UnconnectedPeerException(Throwable e) {
super(e);
}

public UnconnectedPeerException(String m, Throwable e) {
super(m, e);
}

public UnconnectedPeerException(Throwable e, String m, Object... args) {
super(String.format(m, args), e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*******************************************************************************/
package org.eclipse.leshan.integration.tests;

import static org.junit.Assert.assertEquals;

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
Expand Down Expand Up @@ -132,7 +134,7 @@ public void async_send_without_acknowleged() throws Exception {
// Request should timedout in ~1s we don't send ACK
callback.waitForResponse(1500);
Assert.assertTrue("we should timeout", callback.getException() instanceof TimeoutException);

assertEquals(TimeoutException.Type.COAP_TIMEOUT, ((TimeoutException) callback.getException()).getType());
}

@Test
Expand All @@ -157,5 +159,6 @@ public void async_send_with_acknowleged_request_without_response() throws Except
Assert.assertTrue("we should still wait for response", callback.getException() == null);
callback.waitForResponse(2000);
Assert.assertTrue("we should timeout", callback.getException() instanceof TimeoutException);
assertEquals(TimeoutException.Type.RESPONSE_TIMEOUT, ((TimeoutException) callback.getException()).getType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ protected LeshanServerBuilder createServerBuilder() {
LeshanServerBuilder builder = super.createServerBuilder();
securityStore = new InMemorySecurityStore();
builder.setSecurityStore(securityStore);
Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setMaxRetransmissions(1);
dtlsConfig.setRetransmissionTimeout(300);
builder.setDtlsConfig(dtlsConfig);
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@
import org.eclipse.californium.elements.RawData;
import org.eclipse.californium.elements.auth.PreSharedKeyIdentity;
import org.eclipse.californium.elements.exception.EndpointMismatchException;
import org.eclipse.californium.elements.exception.EndpointUnconnectedException;
import org.eclipse.californium.elements.util.SimpleMessageCallback;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.exception.SendFailedException;
import org.eclipse.leshan.core.request.exception.TimeoutException;
import org.eclipse.leshan.core.request.exception.UnconnectedPeerException;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.EditableSecurityStore;
Expand Down Expand Up @@ -268,6 +269,46 @@ public void server_initiates_dtls_handshake() throws NonUniqueSecurityInfoExcept
assertNotNull(session);
}

@Test
public void server_initiates_dtls_handshake_timeout() throws NonUniqueSecurityInfoException, InterruptedException {
// Create PSK server & start it
helper.createServer(); // default server support PSK
helper.server.start();

// Create PSK Client
helper.createPSKClient();

// Add client credentials to the server
helper.getSecurityStore()
.add(SecurityInfo.newPreSharedKeyInfo(helper.getCurrentEndpoint(), GOOD_PSK_ID, GOOD_PSK_KEY));

// Check for registration
helper.assertClientNotRegisterered();
helper.client.start();
helper.waitForRegistrationAtServerSide(1);
Registration registration = helper.getCurrentRegistration();
helper.assertClientRegisterered();

// Remove DTLS connection at server side.
((DTLSConnector) helper.server.coap().getSecuredEndpoint().getConnector()).clearConnectionState();

// stop client
helper.client.stop(false);

// try to send request synchronously
ReadResponse readResponse = helper.server.send(registration, new ReadRequest(3), 1000);
assertNull(readResponse);

// try to send request asynchronously
Callback<ReadResponse> callback = new Callback<>();
helper.server.send(registration, new ReadRequest(3), 1000, callback, callback);
callback.waitForResponse(1100);
assertTrue(callback.getException() instanceof TimeoutException);
assertEquals(TimeoutException.Type.DTLS_HANDSHAKE_TIMEOUT,
((TimeoutException) callback.getException()).getType());

}

@Test
public void server_does_not_initiate_dtls_handshake_with_queue_mode()
throws NonUniqueSecurityInfoException, InterruptedException {
Expand Down Expand Up @@ -296,8 +337,9 @@ public void server_does_not_initiate_dtls_handshake_with_queue_mode()
try {
helper.server.send(registration, new ReadRequest(3), 1000);
fail("Read request SHOULD have failed");
} catch (SendFailedException e) {
assertTrue(e.getCause() instanceof EndpointUnconnectedException);
} catch (UnconnectedPeerException e) {
// expected result
assertFalse("client is still awake", helper.server.getPresenceService().isClientAwake(registration));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.leshan.core.request.exception.RequestCanceledException;
import org.eclipse.leshan.core.request.exception.RequestRejectedException;
import org.eclipse.leshan.core.request.exception.SendFailedException;
import org.eclipse.leshan.core.request.exception.UnconnectedPeerException;
import org.eclipse.leshan.core.response.ErrorCallback;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.response.ObserveResponse;
Expand Down Expand Up @@ -88,6 +89,7 @@ public CaliforniumLwM2mRequestSender(Endpoint secureEndpoint, Endpoint nonSecure
* @throws RequestCanceledException if the request is cancelled.
* @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
* @throws InvalidResponseException if the response received is malformed.
* @throws UnconnectedPeerException if client is not connected (no dtls connection available).
*/
@Override
public <T extends LwM2mResponse> T send(Registration destination, DownlinkRequest<T> request, long timeout)
Expand Down Expand Up @@ -126,6 +128,7 @@ public <T extends LwM2mResponse> T send(Registration destination, DownlinkReques
* <li>{@link RequestCanceledException} if the request is cancelled.</li>
* <li>{@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.</li>
* <li>{@link InvalidResponseException} if the response received is malformed.</li>
* <li>{@link UnconnectedPeerException} if client is not connected (no dtls connection available).</li>
* <li>{@link TimeoutException} if the timeout expires (see
* https://github.com/eclipse/leshan/wiki/Request-Timeout).</li>
* <li>or any other RuntimeException for unexpected issue.
Expand Down Expand Up @@ -171,6 +174,7 @@ public void onResponse(T response) {
* @throws RequestRejectedException if the request is rejected by foreign peer.
* @throws RequestCanceledException if the request is cancelled.
* @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
* @throws UnconnectedPeerException if client is not connected (no dtls connection available).
*/
@Override
public Response sendCoapRequest(Registration destination, Request coapRequest, long timeoutInMs)
Expand All @@ -197,6 +201,7 @@ public Response sendCoapRequest(Registration destination, Request coapRequest, l
* <li>{@link RequestRejectedException} if the request is rejected by foreign peer.</li>
* <li>{@link RequestCanceledException} if the request is cancelled.</li>
* <li>{@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.</li>
* <li>{@link UnconnectedPeerException} if client is not connected (no dtls connection available).</li>
* <li>{@link TimeoutException} if the timeout expires (see
* https://github.com/eclipse/leshan/wiki/Request-Timeout).</li>
* <li>or any other RuntimeException for unexpected issue.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import org.eclipse.californium.core.coap.Response;
import org.eclipse.leshan.core.californium.CoapResponseCallback;
import org.eclipse.leshan.core.request.exception.ClientSleepingException;
import org.eclipse.leshan.core.request.exception.RequestCanceledException;
import org.eclipse.leshan.core.request.exception.RequestRejectedException;
import org.eclipse.leshan.core.request.exception.SendFailedException;
import org.eclipse.leshan.core.request.exception.UnconnectedPeerException;
import org.eclipse.leshan.core.response.ErrorCallback;
import org.eclipse.leshan.core.response.ResponseCallback;
import org.eclipse.leshan.server.registration.Registration;
Expand Down Expand Up @@ -48,6 +50,7 @@ public interface CoapRequestSender {
* @throws RequestRejectedException if the request is rejected by foreign peer.
* @throws RequestCanceledException if the request is cancelled.
* @throws SendFailedException if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.
* @throws UnconnectedPeerException if client is not connected (no dtls connection available).
* @throws ClientSleepingException if client is currently sleeping.
*/
Response sendCoapRequest(final Registration destination, final Request coapRequest, long timeoutInMs)
Expand All @@ -70,6 +73,7 @@ Response sendCoapRequest(final Registration destination, final Request coapReque
* <li>{@link RequestCanceledException} if the request is cancelled.</li>
* <li>{@link SendFailedException} if the request can not be sent. E.g. error at CoAP or DTLS/UDP layer.</li>
* <li>{@link ClientSleepingException} if client is currently sleeping.</li>
* <li>{@link UnconnectedPeerException} if client is not connected (no dtls connection available).</li>
* <li>{@link TimeoutException} if the timeout expires (see
* https://github.com/eclipse/leshan/wiki/Request-Timeout).</li>
* <li>or any other RuntimeException for unexpected issue.
Expand Down
Loading