Skip to content

Commit

Permalink
Test multi-threaded Netty server support (#94)
Browse files Browse the repository at this point in the history
* Test multi-threaded Netty server support

* Move to another file

* Optimize imports
  • Loading branch information
akolosov-n committed Aug 27, 2024
1 parent a94d3c9 commit 6b29a92
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 2 deletions.
1 change: 1 addition & 0 deletions coap-mbedtls/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ dependencies {
testImplementation(testFixtures(project(":coap-core")))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.3")
testImplementation("io.netty:netty-transport-native-epoll:4.1.112.Final:linux-x86_64")
testImplementation("ch.qos.logback:logback-classic:1.3.14")
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import static com.mbed.coap.utils.Assertions.assertEquals;
import static com.mbed.coap.utils.Networks.localhost;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.opencoap.coap.netty.CoapCodec.EMPTY_RESOLVER;
import static org.opencoap.transport.mbedtls.DtlsTransportContext.DTLS_AUTHENTICATION;
import static org.opencoap.transport.mbedtls.DtlsTransportContext.DTLS_COAP_TO_DATAGRAM_CONVERTER;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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.packet.CoapRequest.post;
import static com.mbed.coap.packet.CoapResponse.ok;
import static com.mbed.coap.packet.Opaque.of;
import static com.mbed.coap.utils.Assertions.assertEquals;
import static com.mbed.coap.utils.Networks.localhost;
import static org.opencoap.coap.netty.CoapCodec.EMPTY_RESOLVER;
import com.mbed.coap.client.CoapClient;
import com.mbed.coap.server.CoapServer;
import com.mbed.coap.server.CoapServerBuilder;
import com.mbed.coap.server.RouterService;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.opencoap.coap.netty.NettyCoapTransport;
import org.opencoap.ssl.PskAuth;
import org.opencoap.ssl.SslConfig;
import org.opencoap.ssl.netty.DtlsChannelHandler;
import org.opencoap.ssl.netty.DtlsClientHandshakeChannelHandler;
import org.opencoap.ssl.transport.SessionWriter;


@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@EnabledOnOs(OS.LINUX)
public class MultithreadedMbedtlsNettyTest {
private final int threads = 4;
private final int serverPort = new Random().nextInt(32000) + 32000;
private final EventLoopGroup eventLoopGroup = new EpollEventLoopGroup(threads, new DefaultThreadFactory("pool", true));

private final SslConfig clientConf = SslConfig.client(new PskAuth("test", of("secret").getBytes()));
private final SslConfig serverConf = SslConfig.server(new PskAuth("test", of("secret").getBytes()));

private final Bootstrap clientBootstrap = new Bootstrap()
.group(eventLoopGroup)
.localAddress(0)
.channel(EpollDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) {
InetSocketAddress destinationAddress = localhost(serverPort);
ch.pipeline().addFirst("DTLS", new DtlsClientHandshakeChannelHandler(clientConf.newContext(destinationAddress), destinationAddress, SessionWriter.NO_OPS));
}
});

private final Bootstrap serverBootstrap = new Bootstrap()
.group(eventLoopGroup)
.localAddress(serverPort)
.channel(EpollDatagramChannel.class)
.option(EpollChannelOption.SO_REUSEPORT, true)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) {
ch.pipeline().addFirst("DTLS", new DtlsChannelHandler(serverConf));
}
});

@AfterAll
void afterAll() {
eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS);
}

@Test
void multi_thread_server() throws Exception {
CoapServerBuilder serverBuilder = CoapServer.builder()
.executor(eventLoopGroup)
.route(RouterService.builder()
.post("/echo", req -> CompletableFuture.supplyAsync(() -> ok(req.getPayload()).build()))
);
List<CoapServer> servers = new ArrayList<>();
for (int i = 0; i < threads; i++) {
servers.add(
serverBuilder
.transport(new NettyCoapTransport(serverBootstrap, EMPTY_RESOLVER))
.build()
.start()
);
}

List<CoapClient> clients = new ArrayList<>();
InetSocketAddress srvAddr = localhost(serverPort);
CoapServerBuilder clientBuilder = CoapServer.builder().executor(eventLoopGroup);
for (int i = 0; i < 100; i++) {
clients.add(
clientBuilder
.transport(new NettyCoapTransport(clientBootstrap, EMPTY_RESOLVER))
.buildClient(srvAddr)
);
}

for (CoapClient client : clients) {
assertEquals(ok("paska"), client.sendSync(post("/echo").payload("paska")));
client.close();
}

for (CoapServer srv : servers) {
srv.stop();
}
}
}
2 changes: 1 addition & 1 deletion coap-mbedtls/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} %.-1level %m %n</pattern>
<pattern>%d{HH:mm:ss} [tid:%10.10thread] %-5level %-80msg [%logger]%n</pattern>
</encoder>
</appender>

Expand Down

0 comments on commit 6b29a92

Please sign in to comment.