This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new b338e20190 Fix telnet ctrl+c issue #13812 (#13921)
b338e20190 is described below

commit b338e20190e0fba01e8e85fd37e74d5bfc20baf1
Author: 有点小邪恶 <[email protected]>
AuthorDate: Fri Mar 22 10:23:34 2024 +0800

    Fix telnet ctrl+c issue #13812 (#13921)
    
    * Fix telnet ctrl+c issue #13812
    
    * fix formatting issue
    
    * add unit test for CtrlCHandler.java
    
    ---------
    
    Co-authored-by: hengyouhai <[email protected]>
---
 .../dubbo/qos/server/handler/CtrlCHandler.java     | 71 +++++++++++++++++++++
 .../qos/server/handler/QosProcessHandler.java      |  1 +
 .../dubbo/qos/server/handler/CtrlCHandlerTest.java | 74 ++++++++++++++++++++++
 3 files changed, 146 insertions(+)

diff --git 
a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/CtrlCHandler.java
 
b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/CtrlCHandler.java
new file mode 100644
index 0000000000..9a4a203a04
--- /dev/null
+++ 
b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/CtrlCHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.qos.server.handler;
+
+import org.apache.dubbo.qos.common.QosConstants;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.util.CharsetUtil;
+
+public class CtrlCHandler extends SimpleChannelInboundHandler<ByteBuf> {
+    /**
+     * When type 'Ctrl+C', telnet client will send the following sequence:
+     * 'FF F4 FF FD 06', it can be divided into two parts:
+     * <p>
+     * 1. 'FF F4' is telnet interrupt process command.
+     * <p>
+     * 2. 'FF FD 06' is  to suppress the output of the process that is to be
+     *    interrupted by the  interrupt process command.
+     * <p>
+     * We need to response with 'FF FC 06' to ignore it and tell the client 
continue
+     * display output.
+     */
+    private byte[] CTRLC_BYTES_SEQUENCE = new byte[] {(byte) 0xff, (byte) 
0xf4, (byte) 0xff, (byte) 0xfd, (byte) 0x06};
+
+    private byte[] RESPONSE_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xfc, 
0x06};
+
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buffer) 
throws Exception {
+        // find ctrl+c
+        final int readerIndex = buffer.readerIndex();
+        for (int i = readerIndex; i < buffer.writerIndex(); i++) {
+            if (buffer.readableBytes() - i < CTRLC_BYTES_SEQUENCE.length) {
+                break;
+            }
+            boolean match = true;
+            for (int j = 0; j < CTRLC_BYTES_SEQUENCE.length; j++) {
+                if (CTRLC_BYTES_SEQUENCE[j] != buffer.getByte(i + j)) {
+                    match = false;
+                    break;
+                }
+            }
+
+            if (match) {
+                buffer.readerIndex(readerIndex + buffer.readableBytes());
+                ctx.writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE));
+                ctx.writeAndFlush(Unpooled.wrappedBuffer(
+                        (QosConstants.BR_STR + 
QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8)));
+
+                return;
+            }
+        }
+        ctx.fireChannelRead(buffer);
+    }
+}
diff --git 
a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
 
b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
index 93b9f1d663..353a2eed7c 100644
--- 
a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
+++ 
b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
@@ -89,6 +89,7 @@ public class QosProcessHandler extends ByteToMessageDecoder {
             p.addLast(new HttpProcessHandler(frameworkModel, 
qosConfiguration));
             p.remove(this);
         } else {
+            p.addLast(new CtrlCHandler());
             p.addLast(new LineBasedFrameDecoder(2048));
             p.addLast(new StringDecoder(CharsetUtil.UTF_8));
             p.addLast(new StringEncoder(CharsetUtil.UTF_8));
diff --git 
a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/CtrlCHandlerTest.java
 
b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/CtrlCHandlerTest.java
new file mode 100644
index 0000000000..0ed5175347
--- /dev/null
+++ 
b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/CtrlCHandlerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.qos.server.handler;
+
+import org.apache.dubbo.qos.common.QosConstants;
+
+import java.nio.charset.StandardCharsets;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.CharsetUtil;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class CtrlCHandlerTest {
+    private byte[] CTRLC_BYTES_SEQUENCE = new byte[] {(byte) 0xff, (byte) 
0xf4, (byte) 0xff, (byte) 0xfd, (byte) 0x06};
+
+    private byte[] RESPONSE_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xfc, 
0x06};
+
+    @Test
+    void testMatchedExactly() throws Exception {
+        ChannelHandlerContext context = mock(ChannelHandlerContext.class);
+        CtrlCHandler ctrlCHandler = new CtrlCHandler();
+        ctrlCHandler.channelRead(context, 
Unpooled.wrappedBuffer(CTRLC_BYTES_SEQUENCE));
+        
verify(context).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE));
+        verify(context)
+                .writeAndFlush(Unpooled.wrappedBuffer(
+                        (QosConstants.BR_STR + 
QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8)));
+    }
+
+    @Test
+    void testMatchedNotExactly() throws Exception {
+        ChannelHandlerContext context = mock(ChannelHandlerContext.class);
+        CtrlCHandler ctrlCHandler = new CtrlCHandler();
+        // before 'ctrl c', user typed other command like 'help'
+        String arbitraryCommand = "help";
+        byte[] commandBytes = 
arbitraryCommand.getBytes(StandardCharsets.UTF_8);
+        ctrlCHandler.channelRead(context, Unpooled.wrappedBuffer(commandBytes, 
CTRLC_BYTES_SEQUENCE));
+        
verify(context).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE));
+        verify(context)
+                .writeAndFlush(Unpooled.wrappedBuffer(
+                        (QosConstants.BR_STR + 
QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8)));
+    }
+
+    @Test
+    void testNotMatched() throws Exception {
+        ChannelHandlerContext context = mock(ChannelHandlerContext.class);
+        CtrlCHandler ctrlCHandler = new CtrlCHandler();
+        String arbitraryCommand = "help" + QosConstants.BR_STR;
+        byte[] commandBytes = 
arbitraryCommand.getBytes(StandardCharsets.UTF_8);
+        ctrlCHandler.channelRead(context, 
Unpooled.wrappedBuffer(commandBytes));
+        verify(context, 
never()).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE));
+        verify(context, never())
+                .writeAndFlush(Unpooled.wrappedBuffer(
+                        (QosConstants.BR_STR + 
QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8)));
+    }
+}

Reply via email to