Ensure we create a copy of the attributes and options when bootstrap a channel (#10965)
Motivation: We need to ensure we copy the attributes and options when bootstrap the channel as otherwise we may change the underlying Entry. This is similar to what was reported in https://github.com/netty/netty-incubator-codec-quic/issues/152. Modifications: - Do a copy and re-use methods - Add unit tests Result: Don't affect attributes / options of channels that are already bootstrapped
This commit is contained in:
parent
305cb1719a
commit
1e87c711b4
@ -52,9 +52,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
|
||||
@SuppressWarnings("unchecked")
|
||||
static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
|
||||
private static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
|
||||
@SuppressWarnings("unchecked")
|
||||
static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
|
||||
private static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
|
||||
|
||||
volatile EventLoopGroup group;
|
||||
@SuppressWarnings("deprecation")
|
||||
@ -386,11 +386,23 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
|
||||
public abstract AbstractBootstrapConfig<B, C> config();
|
||||
|
||||
final Map.Entry<ChannelOption<?>, Object>[] newOptionsArray() {
|
||||
return newOptionsArray(options);
|
||||
}
|
||||
|
||||
static Map.Entry<ChannelOption<?>, Object>[] newOptionsArray(Map<ChannelOption<?>, Object> options) {
|
||||
synchronized (options) {
|
||||
return options.entrySet().toArray(EMPTY_OPTION_ARRAY);
|
||||
return new LinkedHashMap<ChannelOption<?>, Object>(options).entrySet().toArray(EMPTY_OPTION_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
final Map.Entry<AttributeKey<?>, Object>[] newAttributesArray() {
|
||||
return newAttributesArray(attrs0());
|
||||
}
|
||||
|
||||
static Map.Entry<AttributeKey<?>, Object>[] newAttributesArray(Map<AttributeKey<?>, Object> attributes) {
|
||||
return attributes.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
|
||||
}
|
||||
|
||||
final Map<ChannelOption<?>, Object> options0() {
|
||||
return options;
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
|
||||
p.addLast(config.handler());
|
||||
|
||||
setChannelOptions(channel, newOptionsArray(), logger);
|
||||
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
|
||||
setAttributes(channel, newAttributesArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,17 +130,14 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
|
||||
@Override
|
||||
void init(Channel channel) {
|
||||
setChannelOptions(channel, newOptionsArray(), logger);
|
||||
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
|
||||
setAttributes(channel, newAttributesArray());
|
||||
|
||||
ChannelPipeline p = channel.pipeline();
|
||||
|
||||
final EventLoopGroup currentChildGroup = childGroup;
|
||||
final ChannelHandler currentChildHandler = childHandler;
|
||||
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
|
||||
synchronized (childOptions) {
|
||||
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
|
||||
}
|
||||
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
|
||||
final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
|
||||
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);
|
||||
|
||||
p.addLast(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
|
@ -38,6 +38,7 @@ import io.netty.channel.local.LocalServerChannel;
|
||||
import io.netty.resolver.AddressResolver;
|
||||
import io.netty.resolver.AddressResolverGroup;
|
||||
import io.netty.resolver.AbstractAddressResolver;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
@ -51,6 +52,8 @@ import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@ -73,6 +76,28 @@ public class BootstrapTest {
|
||||
groupB.terminationFuture().syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionsCopied() {
|
||||
final Bootstrap bootstrapA = new Bootstrap();
|
||||
bootstrapA.option(ChannelOption.AUTO_READ, true);
|
||||
Map.Entry<ChannelOption<?>, Object>[] channelOptions = bootstrapA.newOptionsArray();
|
||||
bootstrapA.option(ChannelOption.AUTO_READ, false);
|
||||
assertEquals(ChannelOption.AUTO_READ, channelOptions[0].getKey());
|
||||
assertEquals(true, channelOptions[0].getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributesCopied() {
|
||||
AttributeKey<String> key = AttributeKey.valueOf(UUID.randomUUID().toString());
|
||||
String value = "value";
|
||||
final Bootstrap bootstrapA = new Bootstrap();
|
||||
bootstrapA.attr(key, value);
|
||||
Map.Entry<AttributeKey<?>, Object>[] attributesArray = bootstrapA.newAttributesArray();
|
||||
bootstrapA.attr(key, "value2");
|
||||
assertEquals(key, attributesArray[0].getKey());
|
||||
assertEquals(value, attributesArray[0].getValue());
|
||||
}
|
||||
|
||||
@Test(timeout = 10000)
|
||||
public void testBindDeadLock() throws Exception {
|
||||
final Bootstrap bootstrapA = new Bootstrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user