update(qos): support foreign ip whitelist. (#11051)

This commit is contained in:
Koy Zhuang 2022-11-30 14:54:03 +08:00 committed by GitHub
parent d07d89159f
commit 850f719927
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 255 additions and 112 deletions

View File

@ -30,6 +30,8 @@ public interface QosConstants {
String ACCEPT_FOREIGN_IP = "qos.accept.foreign.ip";
String ACCEPT_FOREIGN_IP_WHITELIST = "qos.accept.foreign.ip.whitelist";
String QOS_ENABLE_COMPATIBLE = "qos-enable";
String QOS_HOST_COMPATIBLE = "qos-host";
@ -37,4 +39,6 @@ public interface QosConstants {
String QOS_PORT_COMPATIBLE = "qos-port";
String ACCEPT_FOREIGN_IP_COMPATIBLE = "qos-accept-foreign-ip";
String ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE = "qos-accept-foreign-ip-whitelist";
}

View File

@ -50,6 +50,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.STARTUP_PROBE;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
@ -149,6 +151,11 @@ public class ApplicationConfig extends AbstractConfig {
*/
private Boolean qosAcceptForeignIp;
/**
* When we disable accept foreign ip, support specify foreign ip in the whitelist
*/
private String qosAcceptForeignIpWhitelist;
/**
* Customized parameters
*/
@ -400,6 +407,15 @@ public class ApplicationConfig extends AbstractConfig {
this.qosAcceptForeignIp = qosAcceptForeignIp;
}
@Parameter(key = ACCEPT_FOREIGN_IP_WHITELIST)
public String getQosAcceptForeignIpWhitelist() {
return qosAcceptForeignIpWhitelist;
}
public void setQosAcceptForeignIpWhitelist(String qosAcceptForeignIpWhitelist) {
this.qosAcceptForeignIpWhitelist = qosAcceptForeignIpWhitelist;
}
/**
* The format is the same as the springboot, including: getQosEnableCompatible(), getQosPortCompatible(), getQosAcceptForeignIpCompatible().
*
@ -441,6 +457,15 @@ public class ApplicationConfig extends AbstractConfig {
this.setQosAcceptForeignIp(qosAcceptForeignIp);
}
@Parameter(key = ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE, excluded = true, attribute = false)
public String getQosAcceptForeignIpWhitelistCompatible() {
return this.getQosAcceptForeignIpWhitelist();
}
public void setQosAcceptForeignIpWhitelistCompatible(String qosAcceptForeignIpWhitelist) {
this.setQosAcceptForeignIpWhitelist(qosAcceptForeignIpWhitelist);
}
public Map<String, String> getParameters() {
return parameters;
}

View File

@ -18,6 +18,7 @@
package org.apache.dubbo.config;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT_COMPATIBLE;
@ -140,7 +141,7 @@ public interface Constants {
String MULTI_SERIALIZATION_KEY = "serialize.multiple";
String[] DOT_COMPATIBLE_KEYS = new String[]{QOS_ENABLE_COMPATIBLE, QOS_HOST_COMPATIBLE, QOS_PORT_COMPATIBLE,
ACCEPT_FOREIGN_IP_COMPATIBLE, REGISTRY_TYPE_KEY};
ACCEPT_FOREIGN_IP_COMPATIBLE, ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE, REGISTRY_TYPE_KEY};
String IGNORE_CHECK_KEYS = "ignoreCheckKeys";

View File

@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.qos.common.QosConstants;
import org.apache.dubbo.qos.pu.QosWireProtocol;
import org.apache.dubbo.qos.server.Server;
@ -37,6 +38,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_FAILED_START_SERVER;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT;
@ -113,6 +115,7 @@ public class QosProtocolWrapper implements Protocol, ScopeModelAware {
String host = url.getParameter(QOS_HOST);
int port = url.getParameter(QOS_PORT, QosConstants.DEFAULT_PORT);
boolean acceptForeignIp = Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP, "false"));
String acceptForeignIpWhitelist = url.getParameter(ACCEPT_FOREIGN_IP_WHITELIST, StringUtils.EMPTY_STRING);
Server server = frameworkModel.getBeanFactory().getBean(Server.class);
if (server.isStarted()) {
@ -122,6 +125,7 @@ public class QosProtocolWrapper implements Protocol, ScopeModelAware {
server.setHost(host);
server.setPort(port);
server.setAcceptForeignIp(acceptForeignIp);
server.setAcceptForeignIpWhitelist(acceptForeignIpWhitelist);
server.start();
} catch (Throwable throwable) {

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.qos.pu;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.qos.server.DubboLogo;
import org.apache.dubbo.qos.server.handler.QosProcessHandler;
import org.apache.dubbo.remoting.ChannelHandler;
@ -48,7 +49,7 @@ public class QosWireProtocol extends AbstractWireProtocol implements ScopeModelA
public void configServerProtocolHandler(URL url, ChannelOperator operator) {
// add qosProcess handler
QosProcessHandler handler = new QosProcessHandler(url.getOrDefaultFrameworkModel(),
DubboLogo.DUBBO, false);
DubboLogo.DUBBO, false, StringUtils.EMPTY_STRING);
List<ChannelHandler> handlers = new ArrayList<>();
handlers.add(new ChannelHandlerPretender(handler));
operator.configChannelHandler(handlers);

View File

@ -52,6 +52,7 @@ public class Server {
private int port;
private boolean acceptForeignIp = true;
private String acceptForeignIpWhitelist = StringUtils.EMPTY_STRING;
private EventLoopGroup boss;
@ -97,7 +98,7 @@ public class Server {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new QosProcessHandler(frameworkModel, welcome, acceptForeignIp));
ch.pipeline().addLast(new QosProcessHandler(frameworkModel, welcome, acceptForeignIp, acceptForeignIpWhitelist));
}
});
try {
@ -147,6 +148,10 @@ public class Server {
this.acceptForeignIp = acceptForeignIp;
}
public void setAcceptForeignIpWhitelist(String acceptForeignIpWhitelist) {
this.acceptForeignIpWhitelist = acceptForeignIpWhitelist;
}
public String getWelcome() {
return welcome;
}

View File

@ -0,0 +1,94 @@
/*
* 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 io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.qos.common.QosConstants;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.function.Predicate;
public class ForeignHostPermitHandler extends ChannelHandlerAdapter {
// true means to accept foreign IP
private boolean acceptForeignIp;
// the whitelist of foreign IP when acceptForeignIp = false, the delimiter is colon(,)
// support specific ip and an ip range from CIDR specification
private String acceptForeignIpWhitelist;
private Predicate<String> whitelistPredicate = foreignIp -> false;
public ForeignHostPermitHandler(boolean acceptForeignIp, String foreignIpWhitelist) {
this.acceptForeignIp = acceptForeignIp;
this.acceptForeignIpWhitelist = foreignIpWhitelist;
if (StringUtils.isNotEmpty(foreignIpWhitelist)) {
whitelistPredicate = Arrays.stream(foreignIpWhitelist.split(","))
.map(String::trim)
.filter(StringUtils::isNotEmpty)
.map(foreignIpPattern -> (Predicate<String>) foreignIp -> {
try {
// hard code port to -1
return NetUtils.matchIpExpression(foreignIpPattern, foreignIp, -1);
} catch (UnknownHostException ignore) {
// ignore illegal CIDR specification
}
return false;
})
.reduce(Predicate::or).orElse(s -> false);
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (acceptForeignIp) {
return;
}
final InetAddress inetAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress();
// loopback address, return
if (inetAddress.isLoopbackAddress()) {
return;
}
// the ip is in the whitelist, return
if (checkForeignIpInWhiteList(inetAddress)) {
return;
}
ByteBuf cb = Unpooled.wrappedBuffer((QosConstants.BR_STR + "Foreign Ip Not Permitted, Consider Config It In Whitelist."
+ QosConstants.BR_STR).getBytes());
ctx.writeAndFlush(cb).addListener(ChannelFutureListener.CLOSE);
}
private boolean checkForeignIpInWhiteList(InetAddress inetAddress) {
if (StringUtils.isEmpty(acceptForeignIpWhitelist)) {
return false;
}
final String foreignIp = inetAddress.getHostAddress();
return whitelistPredicate.test(foreignIp);
}
}

View File

@ -1,48 +0,0 @@
/*
* 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.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.net.InetSocketAddress;
public class LocalHostPermitHandler extends ChannelHandlerAdapter {
// true means to accept foreign IP
private boolean acceptForeignIp;
public LocalHostPermitHandler(boolean acceptForeignIp) {
this.acceptForeignIp = acceptForeignIp;
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (!acceptForeignIp) {
if (!((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().isLoopbackAddress()) {
ByteBuf cb = Unpooled.wrappedBuffer((QosConstants.BR_STR + "Foreign Ip Not Permitted."
+ QosConstants.BR_STR).getBytes());
ctx.writeAndFlush(cb).addListener(ChannelFutureListener.CLOSE);
}
}
}
}

View File

@ -44,15 +44,17 @@ public class QosProcessHandler extends ByteToMessageDecoder {
private String welcome;
// true means to accept foreign IP
private boolean acceptForeignIp;
private String acceptForeignIpWhitelist;
private FrameworkModel frameworkModel;
public static final String PROMPT = "dubbo>";
public QosProcessHandler(FrameworkModel frameworkModel, String welcome, boolean acceptForeignIp) {
public QosProcessHandler(FrameworkModel frameworkModel, String welcome, boolean acceptForeignIp, String acceptForeignIpWhitelist) {
this.frameworkModel = frameworkModel;
this.welcome = welcome;
this.acceptForeignIp = acceptForeignIp;
this.acceptForeignIpWhitelist = acceptForeignIpWhitelist;
}
@Override
@ -75,7 +77,7 @@ public class QosProcessHandler extends ByteToMessageDecoder {
final int magic = in.getByte(in.readerIndex());
ChannelPipeline p = ctx.pipeline();
p.addLast(new LocalHostPermitHandler(acceptForeignIp));
p.addLast(new ForeignHostPermitHandler(acceptForeignIp, acceptForeignIpWhitelist));
if (isHttp(magic)) {
// no welcome output for http protocol
if (welcomeFuture != null && welcomeFuture.isCancellable()) {

View File

@ -0,0 +1,110 @@
/*
* 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 io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import org.apache.dubbo.common.utils.StringUtils;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class ForeignHostPermitHandlerTest {
@Test
void shouldShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndEmptyWhiteList() throws Exception {
ChannelHandlerContext context = mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(context.channel()).thenReturn(channel);
InetAddress addr = mock(InetAddress.class);
when(addr.isLoopbackAddress()).thenReturn(false);
InetSocketAddress address = new InetSocketAddress(addr, 12345);
when(channel.remoteAddress()).thenReturn(address);
ChannelFuture future = mock(ChannelFuture.class);
when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future);
ForeignHostPermitHandler handler = new ForeignHostPermitHandler(false, StringUtils.EMPTY_STRING);
handler.handlerAdded(context);
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
verify(context).writeAndFlush(captor.capture());
assertThat(new String(captor.getValue().array()), containsString("Foreign Ip Not Permitted, Consider Config It In Whitelist"));
verify(future).addListener(ChannelFutureListener.CLOSE);
}
@Test
void shouldShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndNotMatchWhiteList() throws Exception {
ChannelHandlerContext context = mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(context.channel()).thenReturn(channel);
InetAddress addr = mock(InetAddress.class);
when(addr.isLoopbackAddress()).thenReturn(false);
when(addr.getHostAddress()).thenReturn("179.23.44.1");
InetSocketAddress address = new InetSocketAddress(addr, 12345);
when(channel.remoteAddress()).thenReturn(address);
ChannelFuture future = mock(ChannelFuture.class);
when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future);
ForeignHostPermitHandler handler = new ForeignHostPermitHandler(false, "175.23.44.1 , 192.168.1.192/26");
handler.handlerAdded(context);
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
verify(context).writeAndFlush(captor.capture());
assertThat(new String(captor.getValue().array()), containsString("Foreign Ip Not Permitted, Consider Config It In Whitelist"));
verify(future).addListener(ChannelFutureListener.CLOSE);
}
@Test
void shouldNotShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndMatchWhiteList() throws Exception {
ChannelHandlerContext context = mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(context.channel()).thenReturn(channel);
InetAddress addr = mock(InetAddress.class);
when(addr.isLoopbackAddress()).thenReturn(false);
when(addr.getHostAddress()).thenReturn("175.23.44.1");
InetSocketAddress address = new InetSocketAddress(addr, 12345);
when(channel.remoteAddress()).thenReturn(address);
ForeignHostPermitHandler handler = new ForeignHostPermitHandler(false, "175.23.44.1, 192.168.1.192/26 ");
handler.handlerAdded(context);
verify(context, never()).writeAndFlush(any());
}
@Test
void shouldNotShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndMatchWhiteListRange() throws Exception {
ChannelHandlerContext context = mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(context.channel()).thenReturn(channel);
InetAddress addr = mock(InetAddress.class);
when(addr.isLoopbackAddress()).thenReturn(false);
when(addr.getHostAddress()).thenReturn("192.168.1.199");
InetSocketAddress address = new InetSocketAddress(addr, 12345);
when(channel.remoteAddress()).thenReturn(address);
ForeignHostPermitHandler handler = new ForeignHostPermitHandler(false, "175.23.44.1, 192.168.1.192/26");
handler.handlerAdded(context);
verify(context, never()).writeAndFlush(any());
}
}

View File

@ -1,56 +0,0 @@
/*
* 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 io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class LocalHostPermitHandlerTest {
@Test
void testHandlerAdded() throws Exception {
ChannelHandlerContext context = mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(context.channel()).thenReturn(channel);
InetAddress addr = mock(InetAddress.class);
when(addr.isLoopbackAddress()).thenReturn(false);
InetSocketAddress address = new InetSocketAddress(addr, 12345);
when(channel.remoteAddress()).thenReturn(address);
ChannelFuture future = mock(ChannelFuture.class);
when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future);
LocalHostPermitHandler handler = new LocalHostPermitHandler(false);
handler.handlerAdded(context);
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
verify(context).writeAndFlush(captor.capture());
assertThat(new String(captor.getValue().array()), containsString("Foreign Ip Not Permitted"));
verify(future).addListener(ChannelFutureListener.CLOSE);
}
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.qos.server.handler;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import io.netty.buffer.ByteBuf;
@ -42,7 +43,7 @@ class QosProcessHandlerTest {
ChannelHandlerContext context = Mockito.mock(ChannelHandlerContext.class);
ChannelPipeline pipeline = Mockito.mock(ChannelPipeline.class);
Mockito.when(context.pipeline()).thenReturn(pipeline);
QosProcessHandler handler = new QosProcessHandler(FrameworkModel.defaultModel(), "welcome", false);
QosProcessHandler handler = new QosProcessHandler(FrameworkModel.defaultModel(), "welcome", false, StringUtils.EMPTY_STRING);
handler.decode(context, buf, Collections.emptyList());
verify(pipeline).addLast(any(HttpServerCodec.class));
verify(pipeline).addLast(any(HttpObjectAggregator.class));
@ -56,7 +57,7 @@ class QosProcessHandlerTest {
ChannelHandlerContext context = Mockito.mock(ChannelHandlerContext.class);
ChannelPipeline pipeline = Mockito.mock(ChannelPipeline.class);
Mockito.when(context.pipeline()).thenReturn(pipeline);
QosProcessHandler handler = new QosProcessHandler(FrameworkModel.defaultModel(), "welcome", false);
QosProcessHandler handler = new QosProcessHandler(FrameworkModel.defaultModel(), "welcome", false, StringUtils.EMPTY_STRING);
handler.decode(context, buf, Collections.emptyList());
verify(pipeline).addLast(any(LineBasedFrameDecoder.class));
verify(pipeline).addLast(any(StringDecoder.class));
@ -66,4 +67,4 @@ class QosProcessHandlerTest {
}
}
}