Skip to content

Commit

Permalink
Make CoAP responses carry DTLS context as well as requests (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
akolosov-n committed Aug 19, 2024
1 parent f4adb67 commit 0a74d7f
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 45 deletions.
8 changes: 4 additions & 4 deletions coap-core/src/main/java/com/mbed/coap/packet/CoapPacket.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -97,11 +97,11 @@ public static CoapPacket from(SeparateResponse resp) {
}

public CoapResponse toCoapResponse() {
return CoapResponse.of(code, payload, options);
return CoapResponse.of(code, payload, options).withContext(this.transportContext);
}

public SeparateResponse toSeparateResponse() {
return toCoapResponse().toSeparate(getToken(), getRemoteAddress(), getTransportContext());
return toCoapResponse().toSeparate(getToken(), getRemoteAddress());
}

public InetSocketAddress getRemoteAddress() {
Expand Down Expand Up @@ -181,7 +181,7 @@ public CoapPacket createResponse(Code responseCode) {

public CoapPacket createResponseFrom(CoapResponse coapResponse) {
CoapPacket response = new CoapPacket(this.getRemoteAddress());
response.setTransportContext(this.transportContext);
response.setTransportContext(this.transportContext.with(coapResponse.getTransContext()));
response.setCode(coapResponse.getCode());
response.setToken(getToken());
response.setPayload(coapResponse.getPayload());
Expand Down
56 changes: 44 additions & 12 deletions coap-core/src/main/java/com/mbed/coap/packet/CoapResponse.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,25 +26,27 @@ public class CoapResponse {
private final Code code;
private final HeaderOptions options;
private final Opaque payload;
private final TransportContext transContext;

private CoapResponse(Code code, Opaque payload, HeaderOptions options) {
private CoapResponse(Code code, Opaque payload, HeaderOptions options, TransportContext transContext) {
this.code = code;
this.payload = Objects.requireNonNull(payload);
this.options = Objects.requireNonNull(options);
this.transContext = Objects.requireNonNull(transContext);
}

// --- STATIC CONSTRUCTORS ---

public static CoapResponse of(Code code) {
return new CoapResponse(code, Opaque.EMPTY, new HeaderOptions());
return new CoapResponse(code, Opaque.EMPTY, new HeaderOptions(), TransportContext.EMPTY);
}

public static CoapResponse of(Code code, Opaque payload) {
return new CoapResponse(code, payload, new HeaderOptions());
return new CoapResponse(code, payload, new HeaderOptions(), TransportContext.EMPTY);
}

public static CoapResponse of(Code code, Opaque payload, HeaderOptions options) {
return new CoapResponse(code, payload, options);
return new CoapResponse(code, payload, options, TransportContext.EMPTY);
}

public static CoapResponse of(Code code, String description) {
Expand Down Expand Up @@ -99,12 +101,21 @@ public String getPayloadString() {
return payload.toUtf8String();
}

@Deprecated
public SeparateResponse toSeparate(Opaque token, InetSocketAddress peerAddress, TransportContext transContext) {
return new SeparateResponse(this, token, peerAddress, transContext);
return new SeparateResponse(this.withContext(transContext), token, peerAddress);
}

public SeparateResponse toSeparate(Opaque token, InetSocketAddress peerAddress) {
return toSeparate(token, peerAddress, TransportContext.EMPTY);
return new SeparateResponse(this, token, peerAddress);
}

public TransportContext getTransContext() {
return transContext;
}

public <T> T getTransContext(TransportContext.Key<T> key) {
return transContext.get(key);
}

@Override
Expand All @@ -116,12 +127,12 @@ public boolean equals(Object o) {
return false;
}
CoapResponse that = (CoapResponse) o;
return Objects.equals(code, that.code) && Objects.equals(options, that.options) && Objects.equals(payload, that.payload);
return Objects.equals(code, that.code) && Objects.equals(options, that.options) && Objects.equals(payload, that.payload) && Objects.equals(transContext, that.transContext);
}

@Override
public int hashCode() {
int result = Objects.hash(code, options);
int result = Objects.hash(code, options, transContext);
result = 31 * result + Objects.hashCode(payload);
return result;
}
Expand All @@ -142,28 +153,34 @@ public String toString() {
// --- IMMUTABLE MODIFIERS ---

public CoapResponse withPayload(Opaque newPayload) {
return new CoapResponse(code, newPayload, options);
return new CoapResponse(code, newPayload, options, transContext);
}

public CoapResponse withOptions(Consumer<CoapOptionsBuilder> optionsFunc) {
CoapOptionsBuilder optionsBuilder = CoapOptionsBuilder.from(options);
optionsFunc.accept(optionsBuilder);
return new CoapResponse(code, payload, optionsBuilder.build());
return new CoapResponse(code, payload, optionsBuilder.build(), transContext);
}

public CoapResponse withContext(TransportContext otherTransContext) {
return new CoapResponse(code, payload, options, transContext.with(otherTransContext));
}

public static class Builder {
private final Code code;
private final CoapOptionsBuilder options = CoapOptionsBuilder.options();
private Opaque payload = Opaque.EMPTY;
private TransportContext transContext = TransportContext.EMPTY;

private Builder(Code code) {
this.code = code;
}

public CoapResponse build() {
return new CoapResponse(code, payload, options.build());
return new CoapResponse(code, payload, options.build(), transContext);
}

@Deprecated
public SeparateResponse toSeparate(Opaque token, InetSocketAddress peerAddress, TransportContext transContext) {
return build().toSeparate(token, peerAddress, transContext);
}
Expand All @@ -190,6 +207,21 @@ public Builder payload(String payload, short contentFormat) {
return payload(Opaque.of(payload));
}

public Builder context(TransportContext newTransportContext) {
this.transContext = newTransportContext;
return this;
}

public <T> Builder addContext(TransportContext.Key<T> key, T value) {
transContext = transContext.with(key, value);
return this;
}

public <T> Builder addContext(TransportContext context) {
transContext = transContext.with(context);
return this;
}

public Builder options(Consumer<CoapOptionsBuilder> builder) {
builder.accept(options);
return this;
Expand Down
23 changes: 13 additions & 10 deletions coap-core/src/main/java/com/mbed/coap/packet/SeparateResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ public class SeparateResponse {
private final CoapResponse response;
private final Opaque token;
private final InetSocketAddress peerAddress;
private final TransportContext transContext;

public SeparateResponse(CoapResponse response, Opaque token, InetSocketAddress peerAddress, TransportContext transContext) {
public SeparateResponse(CoapResponse response, Opaque token, InetSocketAddress peerAddress) {
this.response = Objects.requireNonNull(response);
this.token = Objects.requireNonNull(token);
this.peerAddress = peerAddress;
this.transContext = Objects.requireNonNull(transContext);
}

@Deprecated
public SeparateResponse(CoapResponse response, Opaque token, InetSocketAddress peerAddress, TransportContext transContext) {
this(response.withContext(transContext), token, peerAddress);
}

@Override
Expand All @@ -46,15 +49,15 @@ public InetSocketAddress getPeerAddress() {
}

public TransportContext getTransContext() {
return transContext;
return response.getTransContext();
}

public <T> T getTransContext(TransportContext.Key<T> key) {
return transContext.get(key);
return response.getTransContext().get(key);
}

public <T> T getTransContext(TransportContext.Key<T> key, T defaultValue) {
return transContext.getOrDefault(key, defaultValue);
return response.getTransContext().getOrDefault(key, defaultValue);
}

public Code getCode() {
Expand Down Expand Up @@ -82,19 +85,19 @@ public boolean equals(Object o) {
return false;
}
SeparateResponse that = (SeparateResponse) o;
return Objects.equals(response, that.response) && Objects.equals(token, that.token) && Objects.equals(peerAddress, that.peerAddress) && Objects.equals(transContext, that.transContext);
return Objects.equals(response, that.response) && Objects.equals(token, that.token) && Objects.equals(peerAddress, that.peerAddress);
}

@Override
public int hashCode() {
return Objects.hash(response, token, peerAddress, transContext);
return Objects.hash(response, token, peerAddress);
}

public SeparateResponse withPayload(Opaque newPayload) {
return new SeparateResponse(response.withPayload(newPayload), token, peerAddress, transContext);
return new SeparateResponse(response.withPayload(newPayload), token, peerAddress);
}

public SeparateResponse duplicate() {
return new SeparateResponse(CoapResponse.of(response.getCode(), response.getPayload(), response.options().duplicate()), token, peerAddress, transContext);
return new SeparateResponse(CoapResponse.of(response.getCode(), response.getPayload(), response.options().duplicate()).withContext(response.getTransContext()), token, peerAddress);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -42,6 +42,8 @@


class CoapResponseTest {
private static final TransportContext.Key<String> DUMMY_KEY = new TransportContext.Key<>(null);
private static final TransportContext.Key<String> DUMMY_KEY2 = new TransportContext.Key<>(null);

@Test
void staticFactory() {
Expand Down Expand Up @@ -77,6 +79,7 @@ public void equalsAndHashTest() {
.withGenericPrefabValues(Supplier.class, (Func.Func1<CompletableFuture<CoapResponse>, Supplier>) o -> () -> o)
.withGenericPrefabValues(CompletableFuture.class, (Func.Func1<CoapResponse, CompletableFuture>) coapResponse -> new CompletableFuture<>())
.withPrefabValues(CoapResponse.class, CoapResponse.badRequest().build(), CoapResponse.ok().build())
.withPrefabValues(TransportContext.class, TransportContext.EMPTY, TransportContext.of(TransportContext.NON_CONFIRMABLE, true))
.usingGetClass()
.verify();
}
Expand All @@ -89,6 +92,20 @@ void testToString() {
assertEquals("CoapResponse[400, ContTp:50, pl(13):7b226572726f72223a3132337d]", coapResponse(C400_BAD_REQUEST).payload("{\"error\":123}").contentFormat(CT_APPLICATION_JSON).build().toString());
}

@Test
void shouldAccessTransportContext() {
// when
CoapResponse response = CoapResponse.ok()
.context(TransportContext.EMPTY)
.addContext(DUMMY_KEY, "test")
.addContext(TransportContext.of(DUMMY_KEY2, "test2"))
.build();

// then
assertEquals("test", response.getTransContext(DUMMY_KEY));
assertEquals("test2", response.getTransContext(DUMMY_KEY2));
}

@Nested
class BuilderTest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@
import com.mbed.coap.packet.CoapRequest;
import com.mbed.coap.packet.CoapResponse;
import com.mbed.coap.packet.Code;
import com.mbed.coap.transport.TransportContext;
import com.mbed.coap.utils.Service;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class CoapRequestConverterTest {
private static final TransportContext.Key<Boolean> DUMMY_KEY_IN = new TransportContext.Key<>(false);
private static final TransportContext.Key<Boolean> DUMMY_KEY_OUT = new TransportContext.Key<>(false);
private CoapRequestConverter conv = new CoapRequestConverter(() -> 20);
private Service<CoapRequest, CoapResponse> service = Mockito.mock(Service.class);

Expand Down Expand Up @@ -65,4 +68,19 @@ void shouldConvertNonRequestAndResponse() {

assertEquals(newCoapPacket(LOCAL_5683).non(Code.C205_CONTENT).mid(20).token(13).payload("ok").build(), resp.join());
}

@Test
void shouldUseTransportContextFromResponse() {
given(service.apply(eq(
post("/test2").token(13).payload("test").addContext(DUMMY_KEY_IN, true).to(LOCAL_5683))
)).willReturn(
ok("ok").addContext(DUMMY_KEY_OUT, true).toFuture()
);

CompletableFuture<CoapPacket> resp = conv.apply(
newCoapPacket(LOCAL_5683).mid(1300).token(13).post().uriPath("/test2").payload("test").context(TransportContext.of(DUMMY_KEY_IN, true)).build(), service
);

assertEquals(newCoapPacket(LOCAL_5683).ack(Code.C205_CONTENT).mid(1300).token(13).payload("ok").context(TransportContext.of(DUMMY_KEY_IN, true).with(DUMMY_KEY_OUT, true)).build(), resp.join());
}
}
4 changes: 2 additions & 2 deletions coap-mbedtls/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ description = "coap-mbedtls"

dependencies {
api(project(":coap-core"))
api("io.github.open-coap:kotlin-mbedtls:1.26.0")
api("io.github.open-coap:kotlin-mbedtls:1.27.0")
api("io.github.open-coap:kotlin-mbedtls-netty:1.27.0")

testImplementation(project(":coap-netty"))
testImplementation("io.github.open-coap:kotlin-mbedtls-netty:1.26.0")

testImplementation(testFixtures(project(":coap-core")))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opencoap.transport.mbedtls;

import static com.mbed.coap.transport.TransportContext.NON_CONFIRMABLE;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.opencoap.transport.mbedtls.DtlsTransportContext.DTLS_SESSION_EXPIRATION_HINT;
import com.mbed.coap.packet.CoapRequest;
import com.mbed.coap.packet.CoapResponse;
import com.mbed.coap.transport.TransportContext;
import com.mbed.coap.utils.Filter;
import com.mbed.coap.utils.Service;
import java.util.concurrent.CompletableFuture;

public class DtlsSessionExpirationFilter implements Filter.SimpleFilter<CoapRequest, CoapResponse> {

@Override
public CompletableFuture<CoapResponse> apply(CoapRequest request, Service<CoapRequest, CoapResponse> service) {
if (!request.getTransContext(NON_CONFIRMABLE)) {
return CoapResponse.badRequest().toFuture();
}

return service
.apply(request)
.thenCompose(resp -> completedFuture(resp.withContext(TransportContext.of(DTLS_SESSION_EXPIRATION_HINT, true))));
}
}
Loading

0 comments on commit 0a74d7f

Please sign in to comment.