1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.http.nio.netty.internal; 17 18 import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_DIAGNOSTICS; 19 import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_CONNECTION; 20 import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_INITIAL_WINDOW_SIZE; 21 import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; 22 import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.HTTP2_CONNECTION_PING_TIMEOUT_SECONDS; 23 import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.newSslHandler; 24 import static software.amazon.awssdk.utils.NumericUtils.saturatedCast; 25 import static software.amazon.awssdk.utils.StringUtils.lowerCase; 26 27 import io.netty.buffer.UnpooledByteBufAllocator; 28 import io.netty.channel.Channel; 29 import io.netty.channel.ChannelInitializer; 30 import io.netty.channel.ChannelOption; 31 import io.netty.channel.ChannelPipeline; 32 import io.netty.channel.pool.AbstractChannelPoolHandler; 33 import io.netty.channel.pool.ChannelPool; 34 import io.netty.handler.codec.http.HttpClientCodec; 35 import io.netty.handler.codec.http2.Http2FrameCodec; 36 import io.netty.handler.codec.http2.Http2FrameCodecBuilder; 37 import io.netty.handler.codec.http2.Http2FrameLogger; 38 import io.netty.handler.codec.http2.Http2MultiplexHandler; 39 import io.netty.handler.codec.http2.Http2Settings; 40 import io.netty.handler.logging.LogLevel; 41 import io.netty.handler.logging.LoggingHandler; 42 import io.netty.handler.ssl.SslContext; 43 import io.netty.handler.ssl.SslHandler; 44 import io.netty.handler.ssl.SslProvider; 45 import java.net.URI; 46 import java.time.Duration; 47 import java.util.concurrent.CompletableFuture; 48 import java.util.concurrent.atomic.AtomicReference; 49 import software.amazon.awssdk.annotations.SdkInternalApi; 50 import software.amazon.awssdk.http.Protocol; 51 import software.amazon.awssdk.http.nio.netty.internal.http2.Http2GoAwayEventListener; 52 import software.amazon.awssdk.http.nio.netty.internal.http2.Http2PingHandler; 53 import software.amazon.awssdk.http.nio.netty.internal.http2.Http2SettingsFrameHandler; 54 55 /** 56 * ChannelPoolHandler to configure the client pipeline. 57 */ 58 @SdkInternalApi 59 public final class ChannelPipelineInitializer extends AbstractChannelPoolHandler { 60 private final Protocol protocol; 61 private final SslContext sslCtx; 62 private final SslProvider sslProvider; 63 private final long clientMaxStreams; 64 private final int clientInitialWindowSize; 65 private final Duration healthCheckPingPeriod; 66 private final AtomicReference<ChannelPool> channelPoolRef; 67 private final NettyConfiguration configuration; 68 private final URI poolKey; 69 ChannelPipelineInitializer(Protocol protocol, SslContext sslCtx, SslProvider sslProvider, long clientMaxStreams, int clientInitialWindowSize, Duration healthCheckPingPeriod, AtomicReference<ChannelPool> channelPoolRef, NettyConfiguration configuration, URI poolKey)70 public ChannelPipelineInitializer(Protocol protocol, 71 SslContext sslCtx, 72 SslProvider sslProvider, 73 long clientMaxStreams, 74 int clientInitialWindowSize, 75 Duration healthCheckPingPeriod, 76 AtomicReference<ChannelPool> channelPoolRef, 77 NettyConfiguration configuration, 78 URI poolKey) { 79 this.protocol = protocol; 80 this.sslCtx = sslCtx; 81 this.sslProvider = sslProvider; 82 this.clientMaxStreams = clientMaxStreams; 83 this.clientInitialWindowSize = clientInitialWindowSize; 84 this.healthCheckPingPeriod = healthCheckPingPeriod; 85 this.channelPoolRef = channelPoolRef; 86 this.configuration = configuration; 87 this.poolKey = poolKey; 88 } 89 90 @Override channelCreated(Channel ch)91 public void channelCreated(Channel ch) { 92 ch.attr(CHANNEL_DIAGNOSTICS).set(new ChannelDiagnostics(ch)); 93 ch.attr(PROTOCOL_FUTURE).set(new CompletableFuture<>()); 94 ChannelPipeline pipeline = ch.pipeline(); 95 if (sslCtx != null) { 96 97 SslHandler sslHandler = newSslHandler(sslCtx, ch.alloc(), poolKey.getHost(), poolKey.getPort(), 98 configuration.tlsHandshakeTimeout()); 99 100 pipeline.addLast(sslHandler); 101 pipeline.addLast(SslCloseCompletionEventHandler.getInstance()); 102 103 // Use unpooled allocator to avoid increased heap memory usage from Netty 4.1.43. 104 // See https://github.com/netty/netty/issues/9768 105 if (sslProvider == SslProvider.JDK) { 106 ch.config().setOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT); 107 } 108 } 109 110 if (protocol == Protocol.HTTP2) { 111 configureHttp2(ch, pipeline); 112 } else { 113 configureHttp11(ch, pipeline); 114 } 115 116 if (configuration.reapIdleConnections()) { 117 pipeline.addLast(new IdleConnectionReaperHandler(configuration.idleTimeoutMillis())); 118 } 119 120 if (configuration.connectionTtlMillis() > 0) { 121 pipeline.addLast(new OldConnectionReaperHandler(configuration.connectionTtlMillis())); 122 } 123 124 pipeline.addLast(FutureCancelHandler.getInstance()); 125 126 // Only add it for h1 channel because it does not apply to 127 // h2 connection channel. It will be attached 128 // to stream channels when they are created. 129 if (protocol == Protocol.HTTP1_1) { 130 pipeline.addLast(UnusedChannelExceptionHandler.getInstance()); 131 } 132 133 pipeline.addLast(new LoggingHandler(LogLevel.DEBUG)); 134 } 135 configureHttp2(Channel ch, ChannelPipeline pipeline)136 private void configureHttp2(Channel ch, ChannelPipeline pipeline) { 137 // Using Http2FrameCodecBuilder and Http2MultiplexHandler based on 4.1.37 release notes 138 // https://netty.io/news/2019/06/28/4-1-37-Final.html 139 Http2FrameCodec codec = 140 Http2FrameCodecBuilder.forClient() 141 .headerSensitivityDetector((name, value) -> lowerCase(name.toString()).equals("authorization")) 142 .initialSettings(Http2Settings.defaultSettings().initialWindowSize(clientInitialWindowSize)) 143 .frameLogger(new Http2FrameLogger(LogLevel.DEBUG)) 144 .build(); 145 146 // Connection listeners have higher priority than handlers, in the eyes of the Http2FrameCodec. The Http2FrameCodec will 147 // close any connections when a GOAWAY is received, but we'd like to send a "GOAWAY happened" exception instead of just 148 // closing the connection. Because of this, we use a go-away listener instead of a handler, so that we can send the 149 // exception before the Http2FrameCodec closes the connection itself. 150 codec.connection().addListener(new Http2GoAwayEventListener(ch)); 151 152 pipeline.addLast(codec); 153 ch.attr(HTTP2_CONNECTION).set(codec.connection()); 154 155 ch.attr(HTTP2_INITIAL_WINDOW_SIZE).set(clientInitialWindowSize); 156 pipeline.addLast(new Http2MultiplexHandler(new NoOpChannelInitializer())); 157 pipeline.addLast(new Http2SettingsFrameHandler(ch, clientMaxStreams, channelPoolRef)); 158 if (healthCheckPingPeriod == null) { 159 pipeline.addLast(new Http2PingHandler(HTTP2_CONNECTION_PING_TIMEOUT_SECONDS * 1_000)); 160 } else if (healthCheckPingPeriod.toMillis() > 0) { 161 pipeline.addLast(new Http2PingHandler(saturatedCast(healthCheckPingPeriod.toMillis()))); 162 } 163 } 164 configureHttp11(Channel ch, ChannelPipeline pipeline)165 private void configureHttp11(Channel ch, ChannelPipeline pipeline) { 166 pipeline.addLast(new HttpClientCodec()); 167 ch.attr(PROTOCOL_FUTURE).get().complete(Protocol.HTTP1_1); 168 } 169 170 private static class NoOpChannelInitializer extends ChannelInitializer<Channel> { 171 @Override initChannel(Channel ch)172 protected void initChannel(Channel ch) { 173 } 174 } 175 } 176 177 178