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)));
+ }
+}