Skip to content

Commit 32dd156

Browse files
feat(coap-core): Configure custom recognized critical options in CoapServerBuilder (#119)
1 parent 284d6fa commit 32dd156

7 files changed

Lines changed: 84 additions & 14 deletions

File tree

coap-core/src/main/java/com/mbed/coap/packet/BasicHeaderOptions.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,6 +23,7 @@
2323
import java.io.InputStream;
2424
import java.io.OutputStream;
2525
import java.util.Arrays;
26+
import java.util.Collection;
2627
import java.util.Collections;
2728
import java.util.HashMap;
2829
import java.util.LinkedList;
@@ -139,11 +140,15 @@ public Opaque getCustomOption(Integer optNumber) {
139140
}
140141

141142
public boolean containsUnrecognisedCriticalOption() {
143+
return containsUnrecognisedCriticalOption(Collections.emptySet());
144+
}
145+
146+
public boolean containsUnrecognisedCriticalOption(final Collection<Integer> customOptions) {
142147
if (unrecognizedOptions == null) {
143148
return false;
144149
}
145150
for (int tp : unrecognizedOptions.keySet()) {
146-
if (isCritical(tp)) {
151+
if (!customOptions.contains(tp) && isCritical(tp)) {
147152
return true;
148153
}
149154
}

coap-core/src/main/java/com/mbed/coap/server/CoapServerBuilder.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,6 +56,8 @@
5656
import java.io.IOException;
5757
import java.net.InetSocketAddress;
5858
import java.time.Duration;
59+
import java.util.Collection;
60+
import java.util.Collections;
5961
import java.util.List;
6062
import java.util.concurrent.Executors;
6163
import java.util.concurrent.ScheduledExecutorService;
@@ -85,6 +87,7 @@ public final class CoapServerBuilder {
8587
private ObservationsStore observationStore = ObservationsStore.ALWAYS_EMPTY;
8688
private RequestTagSupplier requestTagSupplier = RequestTagSupplier.createSequential();
8789
private boolean isTransportLoggingEnabled = true;
90+
private Collection<Integer> recognizedCustomOptions = Collections.emptySet();
8891

8992
CoapServerBuilder() {
9093
}
@@ -228,6 +231,17 @@ public CoapServerBuilder transportLogging(boolean isTransportLoggingEnabled) {
228231
return this;
229232
}
230233

234+
/**
235+
* Sets the collection of recognized custom critical CoAP option numbers.
236+
*
237+
* @param recognizedCustomOptions a collection of integer option numbers to be recognized as custom options
238+
* @return this builder instance for method chaining
239+
*/
240+
public CoapServerBuilder recognizedCustomOptions(Collection<Integer> recognizedCustomOptions) {
241+
this.recognizedCustomOptions = requireNonNull(recognizedCustomOptions);
242+
return this;
243+
}
244+
231245
public CoapServer build() {
232246
CoapTransport realTransport = requireNonNull(this.coapTransport.get(), "Missing transport");
233247
CoapTransport coapTransport = isTransportLoggingEnabled ? LoggingCoapTransport.wrap(realTransport) : realTransport;
@@ -273,7 +287,7 @@ public CoapServer build() {
273287
.andThen(new CoapRequestConverter(midSupplier))
274288
.andThen(inboundRequestFilter)
275289
.andThen(new RescueFilter())
276-
.andThen(new CriticalOptionVerifier())
290+
.andThen(new CriticalOptionVerifier(recognizedCustomOptions))
277291
.andThen(new BlockWiseIncomingFilter(capabilities(), maxIncomingBlockTransferSize))
278292
.andThen(routeFilter)
279293
.then(route);

coap-core/src/main/java/com/mbed/coap/server/CriticalOptionVerifier.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,13 +22,25 @@
2222
import com.mbed.coap.packet.Code;
2323
import com.mbed.coap.utils.Filter;
2424
import com.mbed.coap.utils.Service;
25+
import java.util.Collection;
26+
import java.util.Collections;
2527
import java.util.concurrent.CompletableFuture;
2628

2729
class CriticalOptionVerifier implements Filter.SimpleFilter<CoapRequest, CoapResponse> {
2830

31+
private final Collection<Integer> recognizedCustomOptions;
32+
33+
CriticalOptionVerifier() {
34+
this.recognizedCustomOptions = Collections.emptySet();
35+
}
36+
37+
CriticalOptionVerifier(final Collection<Integer> recognizedCustomOptions) {
38+
this.recognizedCustomOptions = recognizedCustomOptions;
39+
}
40+
2941
@Override
3042
public CompletableFuture<CoapResponse> apply(CoapRequest request, Service<CoapRequest, CoapResponse> service) {
31-
if (request.options().containsUnrecognisedCriticalOption()) {
43+
if (request.options().containsUnrecognisedCriticalOption(recognizedCustomOptions)) {
3244
return coapResponse(Code.C402_BAD_OPTION).toFuture();
3345
}
3446
return service.apply(request);

coap-core/src/test/java/com/mbed/coap/packet/HeaderOptionsTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,6 +35,7 @@
3535
import java.io.IOException;
3636
import java.io.OutputStream;
3737
import java.util.Arrays;
38+
import java.util.Collections;
3839
import nl.jqno.equalsverifier.EqualsVerifier;
3940
import nl.jqno.equalsverifier.Warning;
4041
import org.junit.jupiter.api.Test;
@@ -422,6 +423,9 @@ public void criticalOptTest() throws Exception {
422423

423424
h.put(1001, Opaque.of("foo"));
424425
assertTrue(h.containsUnrecognisedCriticalOption());
426+
427+
h.put(1001, Opaque.of("foo"));
428+
assertFalse(h.containsUnrecognisedCriticalOption(Collections.singleton(1001)));
425429
}
426430

427431
@Test

coap-core/src/test/java/com/mbed/coap/server/CoapServerBuilderTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -60,6 +60,13 @@ public void shouldFail_whenIllegalTimeoutValue() throws Exception {
6060
);
6161
}
6262

63+
@Test
64+
public void shouldFail_whenNullRecognizedCustomOptionsValue() throws Exception {
65+
assertThrows(NullPointerException.class, () ->
66+
CoapServer.builder().recognizedCustomOptions(null)
67+
);
68+
}
69+
6370
@Test
6471
public void shouldReuseBuilder() throws Exception {
6572
CoapServer server = new CoapServerBuilder()

coap-core/src/test/java/com/mbed/coap/server/CriticalOptionVerifierTest.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* SPDX-License-Identifier: Apache-2.0
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,19 +22,33 @@
2222
import com.mbed.coap.packet.CoapResponse;
2323
import com.mbed.coap.packet.Code;
2424
import com.mbed.coap.packet.Opaque;
25+
import com.mbed.coap.utils.Service;
26+
import java.util.HashSet;
27+
import java.util.Set;
2528
import java.util.concurrent.CompletableFuture;
2629
import org.junit.jupiter.api.Test;
2730

2831
class CriticalOptionVerifierTest {
2932

30-
private final CriticalOptionVerifier filter = new CriticalOptionVerifier();
31-
3233
@Test
3334
void shouldReturnBadOptionWhenUnrecognizedCriticalOption() {
35+
final CriticalOptionVerifier filter = new CriticalOptionVerifier();
36+
3437
CoapRequest req = get("/test").options(o -> o.custom(1001, Opaque.of("foo"))).build();
3538

3639
CompletableFuture<CoapResponse> resp = filter.apply(req, null);
3740

3841
assertEquals(of(Code.C402_BAD_OPTION), resp.join());
3942
}
40-
}
43+
44+
@Test
45+
void shouldReturnNotBadOptionWhenCustomCriticalOption() {
46+
final CriticalOptionVerifier filter = new CriticalOptionVerifier(new HashSet<Integer>() {{ add(1001); }});
47+
48+
CoapRequest req = get("/test").options(o -> o.custom(1001, Opaque.of("foo"))).build();
49+
50+
CompletableFuture<CoapResponse> resp = filter.apply(req, coapRequest -> CompletableFuture.completedFuture(of(Code.C205_CONTENT)));
51+
52+
assertEquals(of(Code.C205_CONTENT), resp.join());
53+
}
54+
}

coap-tcp/src/main/java/com/mbed/coap/server/CoapServerBuilderForTcp.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
2+
* Copyright (C) 2022-2025 java-coap contributors (https://github.com/open-coap/java-coap)
33
* Copyright (C) 2011-2021 ARM Limited. All rights reserved.
44
* SPDX-License-Identifier: Apache-2.0
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,6 +43,8 @@
4343
import com.mbed.coap.utils.Service;
4444
import java.io.IOException;
4545
import java.net.InetSocketAddress;
46+
import java.util.Collection;
47+
import java.util.Collections;
4648
import java.util.Objects;
4749
import java.util.function.Function;
4850

@@ -59,6 +61,7 @@ public class CoapServerBuilderForTcp {
5961
private NotificationsReceiver notificationsReceiver = NotificationsReceiver.REJECT_ALL;
6062
private ObservationsStore observationsStore = ObservationsStore.ALWAYS_EMPTY;
6163
private Boolean isTransportLoggingEnabled = true;
64+
private Collection<Integer> recognizedCustomOptions = Collections.emptySet();
6265

6366
CoapServerBuilderForTcp() {
6467
csmStorage = new CapabilitiesStorageImpl();
@@ -135,6 +138,17 @@ public CoapServerBuilderForTcp transportLogging(Boolean transportLogging) {
135138
return this;
136139
}
137140

141+
/**
142+
* Sets the collection of recognized custom critical CoAP option numbers.
143+
*
144+
* @param recognizedCustomOptions a collection of integer option numbers to be recognized as custom options
145+
* @return this builder instance for method chaining
146+
*/
147+
public CoapServerBuilderForTcp recognizedCustomOptions(Collection<Integer> recognizedCustomOptions) {
148+
this.recognizedCustomOptions = requireNonNull(recognizedCustomOptions);
149+
return this;
150+
}
151+
138152
public CoapClient buildClient(InetSocketAddress target) throws IOException {
139153
return CoapClient.create(target, build().start(), r -> r.getCode() == Code.C703_PONG);
140154
}
@@ -151,7 +165,7 @@ public CoapServer build() {
151165

152166
// INBOUND
153167
Service<CoapRequest, CoapResponse> inboundService = new RescueFilter()
154-
.andThenIf(hasRoute(), new CriticalOptionVerifier())
168+
.andThenIf(hasRoute(), new CriticalOptionVerifier(recognizedCustomOptions))
155169
.andThenIf(hasRoute(), new BlockWiseIncomingFilter(capabilities(), maxIncomingBlockTransferSize))
156170
.andThen(routeFilter)
157171
.then(route);

0 commit comments

Comments
 (0)