From e92021926a812888b5138fd7619d4fc800de6bf6 Mon Sep 17 00:00:00 2001 From: Magdalena Kundera Date: Wed, 15 May 2024 08:59:49 +0200 Subject: [PATCH] Implementation of timestamped value for Observe Response --- .../tests/lockstep/LockStepTest.java | 42 +++++++++++++- .../request/LwM2mResponseBuilder.java | 57 +++++++++++-------- .../server/request/LwM2mResponseBuilder.java | 45 +++++++++------ 3 files changed, 99 insertions(+), 45 deletions(-) diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java index 6f24d123a3..9274b7ecca 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java @@ -458,8 +458,8 @@ public void read_timestamped(String givenServerEndpointProvider) throws Exceptio // send read request Future future = Executors.newSingleThreadExecutor().submit(() -> { - // send a request with 3 seconds timeout - return server.send(registration, new ReadRequest(ContentFormat.SENML_JSON, 1, 0, 1), 3000); + // send a request with 2 seconds timeout + return server.send(registration, new ReadRequest(ContentFormat.SENML_JSON, 1, 0, 1), 2000); }); // wait for request and send response @@ -468,7 +468,43 @@ public void read_timestamped(String givenServerEndpointProvider) throws Exceptio .payload(payload, ContentFormat.SENML_JSON_CODE).go(); // check response received at server side - ReadResponse response = future.get(3, TimeUnit.SECONDS); + ReadResponse response = future.get(2, TimeUnit.SECONDS); + assertThat(response.getTimestampedLwM2mNode()).isEqualTo(timestampedNode); + } + + @TestAllTransportLayer + public void observe_timestamped(String givenServerEndpointProvider) throws Exception { + + // register client + LockStepLwM2mClient client = new LockStepLwM2mClient(server.getEndpoint(Protocol.COAP).getURI()); + Token token = client + .sendLwM2mRequest(new RegisterRequest(client.getEndpointName(), 60l, "1.1", EnumSet.of(BindingMode.U), + null, null, linkParser.parseCoreLinkFormat(",,".getBytes()), null)); + client.expectResponse().token(token).go(); + server.waitForNewRegistrationOf(client.getEndpointName()); + Registration registration = server.getRegistrationService().getByEndpoint(client.getEndpointName()); + + // create timestamped data + LwM2mEncoder encoder = new DefaultLwM2mEncoder(); + Instant t1 = Instant.now(); + LwM2mSingleResource resource = LwM2mSingleResource.newIntegerResource(1, 3600); + TimestampedLwM2mNode timestampedNode = new TimestampedLwM2mNode(t1, resource); + byte[] payload = encoder.encodeTimestampedData(Arrays.asList(timestampedNode), ContentFormat.SENML_JSON, + new LwM2mPath("/1/0/1"), client.getLwM2mModel()); + + // send observe request + Future future = Executors.newSingleThreadExecutor().submit(() -> { + // send a request with 2 seconds timeout + return server.send(registration, new ObserveRequest(ContentFormat.SENML_JSON, 1, 0, 1), 2000); + }); + + // wait for request and send response + client.expectRequest().storeToken("TKN").storeMID("MID").go(); + client.sendResponse(Type.ACK, ResponseCode.CONTENT).loadMID("MID").loadToken("TKN") + .payload(payload, ContentFormat.SENML_JSON_CODE).go(); + + // check response received at server side + ObserveResponse response = future.get(2, TimeUnit.SECONDS); assertThat(response.getTimestampedLwM2mNode()).isEqualTo(timestampedNode); } } diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java index 8be8a38b13..159d074d01 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java @@ -122,9 +122,9 @@ public void visit(ReadRequest request) { coapResponse.getPayloadString(), coapResponse); } else if (isResponseCodeContent()) { // handle success response - TimestampedLwM2mNode timestampedNode = decodeCoapTimestampedResponse(request.getPath(), coapResponse, + List timestampedNodes = decodeCoapTimestampedResponse(request.getPath(), coapResponse, request, clientEndpoint); - lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, null, timestampedNode, null, coapResponse); + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, null, timestampedNodes.get(0), null, coapResponse); } else { // handle unexpected response: handleUnexpectedResponseCode(clientEndpoint, request, coapResponse); @@ -249,30 +249,39 @@ public void visit(ObserveRequest request) { } else if (isResponseCodeContent() // This is for backward compatibility, when the spec say notification used CHANGED code || isResponseCodeChanged()) { + // handle success response: - LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); - SingleObservation observation = null; - if (coapResponse.getOptions().hasObserve()) { + List timestampedNodes = decodeCoapTimestampedResponse(request.getPath(), coapResponse, + request, clientEndpoint); + if (timestampedNodes != null && !timestampedNodes.isEmpty()) { + lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), null, timestampedNodes, + null, null, coapResponse); + } else { - /* - * Note: When using OSCORE and Observe the first coapRequest sent to register an observation can have - * its Token missing here. Is this because OSCORE re-creates the request before sending? When looking in - * Wireshark all messages have a Token as they should. The lines below fixes this by taking the Token - * from the response that came to the request (since the request actually has a Token when going out the - * response will have the same correct Token. - * - * TODO OSCORE : This should probably not be done here. should we fix this ? should we check if oscore - * is used ? - */ - if (coapRequest.getTokenBytes() == null) { - coapRequest.setToken(coapResponse.getTokenBytes()); + LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); + SingleObservation observation = null; + if (coapResponse.getOptions().hasObserve()) { + + /* + * Note: When using OSCORE and Observe the first coapRequest sent to register an observation can + * have its Token missing here. Is this because OSCORE re-creates the request before sending? When + * looking in Wireshark all messages have a Token as they should. The lines below fixes this by + * taking the Token from the response that came to the request (since the request actually has a + * Token when going out the response will have the same correct Token. + * + * TODO OSCORE : This should probably not be done here. should we fix this ? should we check if + * oscore is used ? + */ + if (coapRequest.getTokenBytes() == null) { + coapRequest.setToken(coapResponse.getTokenBytes()); + } + + // observe request successful + observation = ObserveUtil.createLwM2mObservation(coapRequest); } - - // observe request successful - observation = ObserveUtil.createLwM2mObservation(coapRequest); + lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, + observation, null, coapResponse); } - lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, observation, - null, coapResponse); } else { // handle unexpected response: handleUnexpectedResponseCode(clientEndpoint, request, coapResponse); @@ -490,7 +499,7 @@ private Map decodeCompositeCoapResponse(List pa } } - private TimestampedLwM2mNode decodeCoapTimestampedResponse(LwM2mPath path, Response coapResponse, + private List decodeCoapTimestampedResponse(LwM2mPath path, Response coapResponse, LwM2mRequest request, String endpoint) { List timestampedNodes = null; try { @@ -501,7 +510,7 @@ private TimestampedLwM2mNode decodeCoapTimestampedResponse(LwM2mPath path, Respo "Unable to decode response payload of request [%s] from client [%s] : should receive only 1 timestamped node but received %s", request, endpoint, timestampedNodes.size()); } - return timestampedNodes.get(0); + return timestampedNodes; } catch (CodecException e) { handleCodecException(e, request, coapResponse, endpoint); return null; // should not happen as handleCodecException raise exception diff --git a/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java b/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java index c207ceafb6..7b801daec9 100644 --- a/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java +++ b/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java @@ -124,9 +124,9 @@ public void visit(ReadRequest request) { coapResponse.getPayloadString(), coapResponse); } else if (isResponseCodeContent()) { // handle success response with timestamped node - TimestampedLwM2mNode timestampedNode = decodeCoapTimestampedResponse(request.getPath(), coapResponse, + List timestampedNodes = decodeCoapTimestampedResponse(request.getPath(), coapResponse, request, clientEndpoint); - lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, null, timestampedNode, null, coapResponse); + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, null, timestampedNodes.get(0), null, coapResponse); } else { // handle unexpected response: handleUnexpectedResponseCode(clientEndpoint, request, coapResponse); @@ -256,23 +256,32 @@ public void visit(ObserveRequest request) { // This is for backward compatibility, when the spec say notification used CHANGED code || isResponseCodeChanged()) { // handle success response: - LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); - if (coapResponse.options().getObserve() != null) { - // Observe relation established - Observation observation = coapRequest.getTransContext().get(LwM2mKeys.LESHAN_OBSERVATION); - if (observation instanceof SingleObservation) { - lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, - (SingleObservation) observation, null, coapResponse); + List timestampedNodes = decodeCoapTimestampedResponse(request.getPath(), coapResponse, + request, clientEndpoint); + if (timestampedNodes != null && !timestampedNodes.isEmpty()) { + lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), null, timestampedNodes, + null, null, coapResponse); + } else { + + LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); + + if (coapResponse.options().getObserve() != null) { + // Observe relation established + Observation observation = coapRequest.getTransContext().get(LwM2mKeys.LESHAN_OBSERVATION); + if (observation instanceof SingleObservation) { + lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, + (SingleObservation) observation, null, coapResponse); + } else { + throw new IllegalStateException(String.format( + "A Single Observation is expected in coapRequest transport Context, but was %s", + observation == null ? "null" : observation.getClass().getSimpleName())); + } } else { - throw new IllegalStateException(String.format( - "A Single Observation is expected in coapRequest transport Context, but was %s", - observation == null ? "null" : observation.getClass().getSimpleName())); + // Observe relation NOTestablished + lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, + null, null, coapResponse); } - } else { - // Observe relation NOTestablished - lwM2mresponse = new ObserveResponse(toLwM2mResponseCode(coapResponse.getCode()), content, null, null, - null, coapResponse); } } else { // handle unexpected response: @@ -505,7 +514,7 @@ private Map decodeCompositeCoapResponse(List pa } } - private TimestampedLwM2mNode decodeCoapTimestampedResponse(LwM2mPath path, CoapResponse coapResponse, + private List decodeCoapTimestampedResponse(LwM2mPath path, CoapResponse coapResponse, LwM2mRequest request, String endpoint) { List timestampedNodes = null; try { @@ -517,7 +526,7 @@ private TimestampedLwM2mNode decodeCoapTimestampedResponse(LwM2mPath path, CoapR "Unable to decode response payload of request [%s] from client [%s] : should receive only 1 timestamped node but received %s", request, endpoint, timestampedNodes.size()); } - return timestampedNodes.get(0); + return timestampedNodes; } catch (CodecException e) { handleCodecException(e, request, coapResponse, endpoint); return null; // should not happen as handleCodecException raise exception