Skip to content

Safely declare Netty AttributeKeys #1887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,14 @@
<property name="ignoreComments" value="true"/>
</module>

<!-- Checks that we don't use AttributeKey.newInstance directly -->
<module name="Regexp">
<property name="format" value="AttributeKey\.newInstance"/>
<property name="illegalPattern" value="true"/>
<property name="message" value="Use NettyUtils.getOrCreateAttributeKey to safely declare AttributeKeys"/>
<property name="ignoreComments" value="true"/>
</module>

<!-- Checks that we don't use plural enum names -->
<module name="software.amazon.awssdk.buildtools.checkstyle.PluralEnumNames"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.http2.PingTracker;
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils;

/**
* Keys for attributes attached via {@link io.netty.channel.Channel#attr(AttributeKey)}.
Expand All @@ -36,70 +37,71 @@ public final class ChannelAttributeKey {
/**
* Future that when a protocol (http/1.1 or h2) has been selected.
*/
public static final AttributeKey<CompletableFuture<Protocol>> PROTOCOL_FUTURE = AttributeKey.newInstance(
public static final AttributeKey<CompletableFuture<Protocol>> PROTOCOL_FUTURE = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.protocolFuture");

/**
* Reference to {@link Http2MultiplexedChannelPool} which stores information about leased streams for a multiplexed
* connection.
*/
public static final AttributeKey<Http2MultiplexedChannelPool> HTTP2_MULTIPLEXED_CHANNEL_POOL = AttributeKey.newInstance(
"aws.http.nio.netty.async.http2MultiplexedChannelPool");
public static final AttributeKey<Http2MultiplexedChannelPool> HTTP2_MULTIPLEXED_CHANNEL_POOL =
NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.http2MultiplexedChannelPool");

public static final AttributeKey<PingTracker> PING_TRACKER =
AttributeKey.newInstance("aws.http.nio.netty.async.h2.pingTracker");
NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.h2.pingTracker");

public static final AttributeKey<Http2Connection> HTTP2_CONNECTION =
AttributeKey.newInstance("aws.http.nio.netty.async.http2Connection");
NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.http2Connection");

public static final AttributeKey<Integer> HTTP2_INITIAL_WINDOW_SIZE =
AttributeKey.newInstance("aws.http.nio.netty.async.http2InitialWindowSize");
NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.http2InitialWindowSize");

/**
* Value of the MAX_CONCURRENT_STREAMS from the server's SETTING frame.
*/
public static final AttributeKey<Long> MAX_CONCURRENT_STREAMS = AttributeKey.newInstance(
public static final AttributeKey<Long> MAX_CONCURRENT_STREAMS = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.maxConcurrentStreams");

/**
* {@link AttributeKey} to keep track of whether we should close the connection after this request
* has completed.
*/
static final AttributeKey<Boolean> KEEP_ALIVE = AttributeKey.newInstance("aws.http.nio.netty.async.keepAlive");
static final AttributeKey<Boolean> KEEP_ALIVE = NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.keepAlive");

/**
* Attribute key for {@link RequestContext}.
*/
static final AttributeKey<RequestContext> REQUEST_CONTEXT_KEY = AttributeKey.newInstance(
static final AttributeKey<RequestContext> REQUEST_CONTEXT_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.requestContext");

static final AttributeKey<Subscriber<? super ByteBuffer>> SUBSCRIBER_KEY = AttributeKey.newInstance(
static final AttributeKey<Subscriber<? super ByteBuffer>> SUBSCRIBER_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.subscriber");

static final AttributeKey<Boolean> RESPONSE_COMPLETE_KEY = AttributeKey.newInstance(
static final AttributeKey<Boolean> RESPONSE_COMPLETE_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.responseComplete");

/**
* {@link AttributeKey} to keep track of whether we have received the {@link LastHttpContent}.
*/
static final AttributeKey<Boolean> LAST_HTTP_CONTENT_RECEIVED_KEY = AttributeKey.newInstance(
static final AttributeKey<Boolean> LAST_HTTP_CONTENT_RECEIVED_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.lastHttpContentReceived");

static final AttributeKey<CompletableFuture<Void>> EXECUTE_FUTURE_KEY = AttributeKey.newInstance(
static final AttributeKey<CompletableFuture<Void>> EXECUTE_FUTURE_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.executeFuture");

static final AttributeKey<Long> EXECUTION_ID_KEY = AttributeKey.newInstance(
static final AttributeKey<Long> EXECUTION_ID_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.executionId");

/**
* Whether the channel is still in use
*/
static final AttributeKey<Boolean> IN_USE = AttributeKey.newInstance("aws.http.nio.netty.async.inUse");
static final AttributeKey<Boolean> IN_USE = NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.inUse");

/**
* Whether the channel should be closed once it is released.
*/
static final AttributeKey<Boolean> CLOSE_ON_RELEASE = AttributeKey.newInstance("aws.http.nio.netty.async.closeOnRelease");
static final AttributeKey<Boolean> CLOSE_ON_RELEASE = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.closeOnRelease");

private ChannelAttributeKey() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.net.URI;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.StringUtils;

Expand All @@ -37,7 +38,7 @@
*/
@SdkInternalApi
public class Http1TunnelConnectionPool implements ChannelPool {
static final AttributeKey<Boolean> TUNNEL_ESTABLISHED_KEY = AttributeKey.newInstance(
static final AttributeKey<Boolean> TUNNEL_ESTABLISHED_KEY = NettyUtils.getOrCreateAttributeKey(
"aws.http.nio.netty.async.Http1TunnelConnectionPool.tunnelEstablished");

private static final Logger log = Logger.loggerFor(Http1TunnelConnectionPool.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils;

/**
* Wrapper around a {@link ChannelPool} to protect it from having the same channel released twice. This can
Expand All @@ -35,7 +36,8 @@
@SdkInternalApi
public class ReleaseOnceChannelPool implements ChannelPool {

private static final AttributeKey<AtomicBoolean> IS_RELEASED = AttributeKey.newInstance("isReleased");
private static final AttributeKey<AtomicBoolean> IS_RELEASED = NettyUtils.getOrCreateAttributeKey(
"software.amazon.awssdk.http.nio.netty.internal.http2.ReleaseOnceChannelPool.isReleased");

private final ChannelPool delegate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey;
import software.amazon.awssdk.http.nio.netty.internal.utils.BetterFixedChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.Validate;

Expand All @@ -72,13 +73,13 @@ public class Http2MultiplexedChannelPool implements ChannelPool {
/**
* Reference to the {@link MultiplexedChannelRecord} on a channel.
*/
private static final AttributeKey<MultiplexedChannelRecord> MULTIPLEXED_CHANNEL = AttributeKey.newInstance(
"software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool.MULTIPLEXED_CHANNEL");
private static final AttributeKey<MultiplexedChannelRecord> MULTIPLEXED_CHANNEL = NettyUtils.getOrCreateAttributeKey(
"software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool.MULTIPLEXED_CHANNEL");

/**
* Whether a parent channel has been released yet. This guards against double-releasing to the delegate connection pool.
*/
private static final AttributeKey<Boolean> RELEASED = AttributeKey.newInstance(
private static final AttributeKey<Boolean> RELEASED = NettyUtils.getOrCreateAttributeKey(
"software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool.RELEASED");

private final ChannelPool connectionPool;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package software.amazon.awssdk.http.nio.netty.internal.utils;

import io.netty.channel.EventLoop;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
Expand Down Expand Up @@ -160,4 +161,16 @@ public static void warnIfNotInEventLoop(EventLoop loop) {
log.warn(() -> "Execution is happening outside of the expected event loop.", exception);
}
}

/**
* @return an {@code AttributeKey} for {@code attr}. This returns an existing instance if it was previously created.
*/
public static <T> AttributeKey<T> getOrCreateAttributeKey(String attr) {
if (AttributeKey.exists(attr)) {
return AttributeKey.valueOf(attr);
}
//CHECKSTYLE:OFF - This is the only place allowed to call AttributeKey.newInstance()
return AttributeKey.newInstance(attr);
//CHECKSTYLE:ON
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
@SdkInternalApi
public class OrderedWriteChannelHandlerContext extends DelegatingChannelHandlerContext {
private static final AttributeKey<Void> ORDERED =
AttributeKey.newInstance("aws.http.nio.netty.async.OrderedWriteChannelHandlerContext.ORDERED");
NettyUtils.getOrCreateAttributeKey("aws.http.nio.netty.async.OrderedWriteChannelHandlerContext.ORDERED");

private OrderedWriteChannelHandlerContext(ChannelHandlerContext delegate) {
super(delegate);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Amazon.com, 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.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.utils;

import static org.assertj.core.api.Assertions.assertThat;

import io.netty.util.AttributeKey;
import org.junit.Test;

public class NettyUtilsTest {
@Test
public void testGetOrCreateAttributeKey_calledTwiceWithSameName_returnsSameInstance() {
String attr = "NettyUtilsTest.Foo";
AttributeKey<String> fooAttr = NettyUtils.getOrCreateAttributeKey(attr);
assertThat(NettyUtils.getOrCreateAttributeKey(attr)).isSameAs(fooAttr);
}
}