Skip to content

Commit

Permalink
Enhance the way we handle InvalidRequest from foreign peer.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Aug 9, 2019
1 parent 680ee7d commit ca15e3d
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 96 deletions.
1 change: 1 addition & 0 deletions leshan-bsserver-demo/logback-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Contributors:
</root>

<logger name="org.eclipse.leshan" level="INFO" />
<logger name="org.eclipse.leshan.core.californium.LwM2mCoapResource" level="DEBUG"/>
<logger name="org.eclipse.leshan.server.security.SecurityCheck" level="DEBUG" />
<logger name="org.eclipse.leshan.server.bootstrap.DefaultBootstrapHandler" level="DEBUG" />
<logger name="org.eclipse.leshan.core.model.LwM2mModel" level="DEBUG" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,28 @@

import static org.eclipse.leshan.core.californium.ResponseCodeUtil.toCoapResponseCode;

import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.MessageObserverAdapter;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.leshan.client.request.ServerIdentity;
import org.eclipse.leshan.client.servers.BootstrapHandler;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.request.BootstrapFinishRequest;
import org.eclipse.leshan.core.response.BootstrapFinishResponse;
import org.eclipse.leshan.core.response.SendableResponse;

/**
* A CoAP {@link Resource} in charge of handling the Bootstrap Finish indication from the bootstrap server.
*/
public class BootstrapResource extends CoapResource {
public class BootstrapResource extends LwM2mCoapResource {

private final BootstrapHandler bootstrapHandler;

public BootstrapResource(BootstrapHandler bootstrapHandler) {
super("bs", false);
super("bs");
this.setVisible(false);
this.bootstrapHandler = bootstrapHandler;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@

import java.util.List;

import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.leshan.Link;
Expand All @@ -37,6 +34,7 @@
import org.eclipse.leshan.client.resource.NotifySender;
import org.eclipse.leshan.client.servers.BootstrapHandler;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
Expand All @@ -57,7 +55,6 @@
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.WriteRequest.Mode;
import org.eclipse.leshan.core.request.exception.InvalidRequestException;
import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
import org.eclipse.leshan.core.response.BootstrapWriteResponse;
import org.eclipse.leshan.core.response.CreateResponse;
Expand All @@ -68,15 +65,11 @@
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteAttributesResponse;
import org.eclipse.leshan.core.response.WriteResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A CoAP {@link Resource} in charge of handling requests for of a lwM2M Object.
*/
public class ObjectResource extends CoapResource implements NotifySender {

private static final Logger LOG = LoggerFactory.getLogger(ObjectResource.class);
public class ObjectResource extends LwM2mCoapResource implements NotifySender {

private final LwM2mObjectEnabler nodeEnabler;
private final BootstrapHandler bootstrapHandler;
Expand All @@ -94,25 +87,6 @@ public ObjectResource(LwM2mObjectEnabler nodeEnabler, BootstrapHandler bootstrap
setObservable(true);
}

@Override
public void handleRequest(Exchange exchange) {
try {
super.handleRequest(exchange);
} catch (InvalidRequestException e) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("InvalidRequestException while handling request(%s) on the %s resource",
exchange.getRequest(), getURI()), e);
}
Response response = new Response(ResponseCode.BAD_REQUEST);
response.setPayload(e.getMessage());
exchange.sendResponse(response);
} catch (Exception e) {
LOG.error(String.format("Exception while handling request(%s) on the %s resource", exchange.getRequest(),
getURI()), e);
exchange.sendResponse(new Response(ResponseCode.INTERNAL_SERVER_ERROR));
}
}

@Override
public void handleGET(CoapExchange exchange) {
ServerIdentity identity = extractServerIdentity(exchange, bootstrapHandler);
Expand Down Expand Up @@ -214,7 +188,7 @@ public void handlePUT(CoapExchange coapExchange) {
LwM2mPath path = new LwM2mPath(URI);

if (!coapExchange.getRequestOptions().hasContentFormat()) {
coapExchange.respond(ResponseCode.BAD_REQUEST, "Content Format is mandatory");
handleInvalidRequest(coapExchange, "Content Format is mandatory");
return;
}

Expand Down Expand Up @@ -247,8 +221,7 @@ public void handlePUT(CoapExchange coapExchange) {

return;
} catch (CodecException e) {
LOG.warn("Unable to decode payload to write", e);
coapExchange.respond(ResponseCode.BAD_REQUEST);
handleInvalidRequest(coapExchange.advanced(), "Unable to decode payload on WRITE", e);
return;
}

Expand Down Expand Up @@ -277,7 +250,7 @@ public void handlePOST(CoapExchange exchange) {

// handle content format for Write (Update) and Create request
if (!exchange.getRequestOptions().hasContentFormat()) {
exchange.respond(ResponseCode.BAD_REQUEST, "Content Format is mandatory");
handleInvalidRequest(exchange, "Content Format is mandatory");
return;
}

Expand All @@ -300,8 +273,7 @@ public void handlePOST(CoapExchange exchange) {
exchange.respond(toCoapResponseCode(response.getCode()));
}
} catch (CodecException e) {
LOG.warn("Unable to decode payload to write", e);
exchange.respond(ResponseCode.BAD_REQUEST);
handleInvalidRequest(exchange.advanced(), "Unable to decode payload on WRITE", e);
}
return;
}
Expand Down Expand Up @@ -332,8 +304,7 @@ public void handlePOST(CoapExchange exchange) {
return;
}
} catch (CodecException e) {
LOG.warn("Unable to decode payload to create", e);
exchange.respond(ResponseCode.BAD_REQUEST);
handleInvalidRequest(exchange.advanced(), "Unable to decode payload on CREATE", e);
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.leshan.client.request.ServerIdentity;
import org.eclipse.leshan.client.servers.BootstrapHandler;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
import org.eclipse.leshan.core.response.BootstrapDeleteResponse;
import org.eclipse.leshan.util.StringUtils;

/**
* A {@link CoapResource} resource in charge of handling Bootstrap Delete requests targeting the "/" URI.
*/
public class RootResource extends CoapResource {
public class RootResource extends LwM2mCoapResource {

private final BootstrapHandler bootstrapHandler;

public RootResource(BootstrapHandler bootstrapHandler) {
super("", false);
super("");
setVisible(false);
this.bootstrapHandler = bootstrapHandler;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ org.slf4j.simpleLogger.showShortLogName=true
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss,SSS
org.slf4j.simpleLogger.defaultLogLevel=WARN
org.slf4j.simpleLogger.log.org.eclipse.leshan=INFO
org.slf4j.simpleLogger.log.org.eclipse.leshan.core.californium.LwM2mCoapResource=DEBUG
org.slf4j.simpleLogger.log.org.eclipse.leshan.server.security.SecurityCheck=DEBUG
org.slf4j.simpleLogger.log.org.eclipse.leshan.core.model.LwM2mModel=TRACE
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,25 @@
import org.eclipse.californium.elements.auth.RawPublicKeyIdentity;
import org.eclipse.californium.elements.auth.X509CertPath;
import org.eclipse.leshan.core.request.Identity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Utility class used to handle Californium {@link EndpointContext} in Leshan.
* <p>
* Able to translate Californium {@link EndpointContext} to Leshan {@link Identity} and vice-versa.
*/
public class EndpointContextUtil {

private static final Logger LOG = LoggerFactory.getLogger(EndpointContextUtil.class);

/**
* Create Leshan {@link Identity} from Californium {@link EndpointContext}.
*
* @param context The Californium {@link EndpointContext} to convert.
* @return The corresponding Leshan {@link Identity}.
* @throws IllegalStateException if we are not able to extract {@link Identity}.
*/
public static Identity extractIdentity(EndpointContext context) {
InetSocketAddress peerAddress = context.getPeerAddress();
Principal senderIdentity = context.getPeerIdentity();
Expand All @@ -48,16 +64,33 @@ public static Identity extractIdentity(EndpointContext context) {
String x509CommonName = extractCN(senderIdentity.getName());
return Identity.x509(peerAddress, x509CommonName);
}
throw new IllegalStateException("Unable to extract sender identity : unexpected type of Principal");
throw new IllegalStateException(
String.format("Unable to extract sender identity : unexpected type of Principal %s [%s]",
senderIdentity.getClass(), senderIdentity.toString()));
}
return Identity.unsecure(peerAddress);
}

/**
* Create californium endpoint context from leshan identity.
* Create Leshan {@link Identity} from Californium {@link EndpointContext}.
*
* @param context The Californium {@link EndpointContext} to convert.
* @return The corresponding Leshan {@link Identity} or <code>null</null> if we didn't succeed to extract Identity.
*/
public static Identity extractIdentitySafely(EndpointContext context) {
try {
return extractIdentity(context);
} catch (RuntimeException e) {
LOG.error("Unable to extract identity", e);
return null;
}
}

/**
* Create Californium {@link EndpointContext} from Leshan {@link Identity}.
*
* @param identity leshan identity received on last registration.
* @return californium endpoint context for leshan identity
* @param identity The Leshan {@link Identity} to convert.
* @return The corresponding Californium {@link EndpointContext}.
*/
public static EndpointContext extractContext(Identity identity) {
Principal peerIdentity = null;
Expand All @@ -77,8 +110,8 @@ public static EndpointContext extractContext(Identity identity) {
/**
* Extract "common name" from "distinguished name".
*
* @param dn distinguished name
* @return common name
* @param dn The distinguished name.
* @return The extracted common name.
* @throws IllegalStateException if no CN is contained in DN.
*/
public static String extractCN(String dn) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2019 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.californium;

import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.leshan.core.request.exception.InvalidRequestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A Common {@link CoapResource} used to handle LWM2M request.
* <p>
* It provides mainly features about error handling.
*/
public class LwM2mCoapResource extends CoapResource {

private static final Logger LOG = LoggerFactory.getLogger(LwM2mCoapResource.class);

/**
* @see CoapResource#CoapResource(String)
*/
public LwM2mCoapResource(String name) {
super(name);
}

@Override
public void handleRequest(Exchange exchange) {
try {
super.handleRequest(exchange);
} catch (InvalidRequestException e) {
handleInvalidRequest(exchange, e.getMessage(), e);
} catch (RuntimeException e) {
Request request = exchange.getRequest();
LOG.error("Exception while handling request [{}] on the resource {} from {}", request, getURI(),
EndpointContextUtil.extractIdentitySafely(request.getSourceContext()), e);
exchange.sendResponse(new Response(ResponseCode.INTERNAL_SERVER_ERROR));
}
}

/**
* Handle an Invalid Request by sending a BAD_REQUEST response and logging the error using debug level.
*
* @param exchange The CoAP exchange linked to the invalid request.
* @param message The error message describing why the request is invalid.
*/
protected void handleInvalidRequest(CoapExchange exchange, String message) {
handleInvalidRequest(exchange.advanced(), message, null);
}

/**
* Handle an Invalid Request by sending a BAD_REQUEST response and logging the error using debug level.
*
* @param exchange The exchange linked to the invalid request.
* @param message The error message describing why the request is invalid.
* @param error An {@link Throwable} raised while we handle try create a LWM2M request from CoAP request.
*/
protected void handleInvalidRequest(Exchange exchange, String message, Throwable error) {
Request request = exchange.getRequest();

// Log error
if (LOG.isDebugEnabled()) {
if (error != null) {
LOG.debug("Invalid request [{}] received on the resource {} from {}", request, getURI(),
EndpointContextUtil.extractIdentitySafely(request.getSourceContext()), error);
} else {
LOG.debug("Invalid request [{}] received on the resource {} from {} : {}", request, getURI(),
EndpointContextUtil.extractIdentitySafely(request.getSourceContext()), message);
}
}

// Send Response
Response response = new Response(ResponseCode.BAD_REQUEST);
if (message != null) {
response.setPayload(message);
response.getOptions().setContentFormat(MediaTypeRegistry.TEXT_PLAIN);
}
exchange.sendResponse(response);
}
}
Loading

0 comments on commit ca15e3d

Please sign in to comment.