From abcd1f18add239a093d22193b966270565271333 Mon Sep 17 00:00:00 2001 From: Jack Cheng Date: Wed, 14 Dec 2022 21:06:30 -0800 Subject: [PATCH] Add Reactor Netty version of Telnet example #761 Provide a corresponding Reactor Netty version of Telnet example as found in Netty project. --- .../examples/tcp/telnet/TelnetClient.java | 68 +++++++++++++ .../examples/tcp/telnet/TelnetServer.java | 96 +++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetClient.java create mode 100644 reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetServer.java diff --git a/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetClient.java b/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetClient.java new file mode 100644 index 0000000000..0cb1921831 --- /dev/null +++ b/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetClient.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020-2022 VMware, Inc. or its affiliates, All Rights Reserved. + * + * 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 + * + * https://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 reactor.netty.examples.tcp.telnet; + +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import reactor.netty.Connection; +import reactor.netty.tcp.TcpClient; +import reactor.netty.tcp.TcpSslContextSpec; + +import java.nio.charset.StandardCharsets; +import java.util.Scanner; + +public class TelnetClient { + static final boolean SECURE = System.getProperty("secure") != null; + static final String HOST = System.getProperty("host", "127.0.0.1"); + static final int PORT = Integer.parseInt(System.getProperty("port", SECURE ? "8992" : "8993")); + static final boolean WIRETAP = System.getProperty("wiretap") != null; + private static final StringDecoder DECODER = new StringDecoder(); + private static final StringEncoder ENCODER = new StringEncoder(); + + public static void main(String... args) { + + + TcpClient client = TcpClient.create().host(HOST).port(PORT) + .doOnConnected(connection -> { + connection.addHandlerLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); + connection.addHandlerLast(DECODER); + connection.addHandlerLast(ENCODER); + }) + .wiretap(WIRETAP) + .handle((in, out) -> in.receiveObject().ofType(String.class).doOnNext(s -> System.out.println(s)).then()); + + if (SECURE) { + TcpSslContextSpec tcpSslContextSpec = TcpSslContextSpec.forClient() + .configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE)); + client = client.secure(spec -> spec.sslContext(tcpSslContextSpec)); + } + + Connection conn = client.connectNow(); + Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name()); + while (scanner.hasNext()) { + String text = scanner.nextLine(); + conn.channel().writeAndFlush(text + '\n'); + if ("bye".equalsIgnoreCase(text)) { + break; + } + + } + conn.onDispose().block(); + } +} diff --git a/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetServer.java b/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetServer.java new file mode 100644 index 0000000000..f322908ab1 --- /dev/null +++ b/reactor-netty-examples/src/main/java/reactor/netty/examples/tcp/telnet/TelnetServer.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020-2022 VMware, Inc. or its affiliates, All Rights Reserved. + * + * 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 + * + * https://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 reactor.netty.examples.tcp.telnet; + +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import reactor.core.Exceptions; +import reactor.core.publisher.Mono; +import reactor.netty.tcp.TcpServer; +import reactor.netty.tcp.TcpSslContextSpec; + +import java.net.InetAddress; +import java.security.cert.CertificateException; +import java.util.Date; + +public class TelnetServer { + private static final StringDecoder DECODER = new StringDecoder(); + private static final StringEncoder ENCODER = new StringEncoder(); + + static final boolean SECURE = System.getProperty("secure") != null; + static final int PORT = Integer.parseInt(System.getProperty("port", SECURE ? "8992" : "8993")); + + static final boolean WIRETAP = System.getProperty("wiretap") != null; + + public static void main(String[] args) throws CertificateException { + + + TcpServer server = TcpServer.create() + .port(PORT) + .wiretap(WIRETAP) + .doOnConnection(connection -> { + connection.addHandlerLast(new DelimiterBasedFrameDecoder(8092, Delimiters.lineDelimiter())); + connection.addHandlerLast(DECODER); + connection.addHandlerLast(ENCODER); + try { + connection.channel().writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n"); + connection.channel().writeAndFlush("It is " + new Date() + " now.\r\n"); + } + catch (Exception e) { + throw Exceptions.propagate(e); + } + }) + .handle((in, out) -> in.receiveObject().ofType(String.class).flatMap(text -> { + String responseText; + boolean close = false; + if (text.isEmpty()) { + responseText = "Please type something. \r\n"; + } + else if ("bye".equalsIgnoreCase(text)) { + responseText = "Have a good day!\r\n"; + close = true; + } + else { + responseText = "Did you say '" + text + "'?\r\n"; + } + + if (close) { + return out.withConnection(connection -> { + connection.channel().writeAndFlush(responseText); + connection.dispose(); + }); + } + + Mono response = Mono.just(responseText); + return out.sendString(response); + } + ) + ); + + if (SECURE) { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + server = server.secure(spec -> spec.sslContext(TcpSslContextSpec.forServer(ssc.certificate(), ssc.privateKey()))); + } + + server.bindNow() + .onDispose() + .block(); + + } +}