All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.http.nio.netty.internal.http2.HttpOrHttp2ChannelPool Maven / Gradle / Ivy

Go to download

A single bundled dependency that includes all service and dependent JARs with third-party libraries relocated to different namespaces.

There is a newer version: 2.5.20
Show newest version
/*
 * Copyright 2010-2018 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.http2;

import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.MAX_CONCURRENT_STREAMS;
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE;
import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.doInEventLoop;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.pool.ChannelPoolHandler;
import io.netty.channel.pool.SimpleChannelPool;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration;
import software.amazon.awssdk.http.nio.netty.internal.utils.BetterFixedChannelPool;

/**
 * Channel pool that establishes an initial connection to determine protocol. Delegates
 * to appropriate channel pool implementation depending on the protocol. This assumes that
 * all connections will be negotiated with the same protocol.
 */
@SdkInternalApi
public class HttpOrHttp2ChannelPool implements ChannelPool {

    private final ChannelPool simpleChannelPool;
    private final int maxConcurrency;
    private final EventLoop eventLoop;
    private final NettyConfiguration configuration;

    private Promise protocolImplPromise;
    private ChannelPool protocolImpl;

    public HttpOrHttp2ChannelPool(Bootstrap bootstrap,
                                  ChannelPoolHandler handler,
                                  int maxConcurrency,
                                  NettyConfiguration configuration) {
        this.simpleChannelPool = new SimpleChannelPool(bootstrap, handler);
        this.maxConcurrency = maxConcurrency;
        this.eventLoop = bootstrap.config().group().next();
        this.configuration = configuration;
    }

    @Override
    public Future acquire() {
        return acquire(new DefaultPromise<>(eventLoop));
    }

    @Override
    public Future acquire(Promise promise) {
        doInEventLoop(eventLoop, () -> acquire0(promise), promise);
        return promise;
    }

    private void acquire0(Promise promise) {
        if (protocolImpl != null) {
            protocolImpl.acquire(promise);
            return;
        }
        if (protocolImplPromise == null) {
            initializeProtocol();
        }
        protocolImplPromise.addListener((GenericFutureListener>) future -> {
            if (future.isSuccess()) {
                future.getNow().acquire(promise);
            } else {
                // Couldn't negotiate protocol, fail this acquire.
                promise.setFailure(future.cause());
            }
        });
    }

    /**
     * Establishes a single connection to initialize the protocol and choose the appropriate {@link ChannelPool} implementation
     * for {@link #protocolImpl}.
     */
    private void initializeProtocol() {
        protocolImplPromise = new DefaultPromise<>(eventLoop);
        simpleChannelPool.acquire()
                         .addListener((GenericFutureListener>) future -> {
                             if (future.isSuccess()) {
                                 Channel newChannel = future.getNow();
                                 newChannel.attr(PROTOCOL_FUTURE).get()
                                           .whenComplete((r, e) -> {
                                               if (e != null) {
                                                   failProtocolImplPromise(e);
                                               } else {
                                                   protocolImplPromise.setSuccess(configureProtocol(newChannel, r));
                                               }
                                           });
                             } else {
                                 failProtocolImplPromise(future.cause());
                             }
                         });
    }

    /**
     * Fail the current protocolImplPromise and null it out so the next acquire will attempt to re-initialize it.
     *
     * @param e Cause of failure.
     */
    private void failProtocolImplPromise(Throwable e) {
        protocolImplPromise.setFailure(e);
        protocolImplPromise = null;
    }

    private ChannelPool configureProtocol(Channel newChannel, Protocol protocol) {
        if (Protocol.HTTP1_1 == protocol) {
            // For HTTP/1.1 we use a traditional channel pool without multiplexing
            protocolImpl = BetterFixedChannelPool.builder()
                                                 .channelPool(simpleChannelPool)
                                                 .executor(eventLoop)
                                                 .acquireTimeoutAction(BetterFixedChannelPool.AcquireTimeoutAction.FAIL)
                                                 .acquireTimeoutMillis(configuration.connectionAcquireTimeoutMillis())
                                                 .maxConnections(maxConcurrency)
                                                 .maxPendingAcquires(configuration.maxPendingConnectionAcquires())
                                                 .build();
        } else {
            ChannelPool h2Pool = new Http2MultiplexedChannelPool(
                simpleChannelPool, eventLoop, newChannel.attr(MAX_CONCURRENT_STREAMS).get());
            protocolImpl = BetterFixedChannelPool.builder()
                                                 .channelPool(h2Pool)
                                                 .executor(eventLoop)
                                                 .acquireTimeoutAction(BetterFixedChannelPool.AcquireTimeoutAction.FAIL)
                                                 .acquireTimeoutMillis(configuration.connectionAcquireTimeoutMillis())
                                                 .maxConnections(maxConcurrency)
                                                 .maxPendingAcquires(configuration.maxPendingConnectionAcquires())
                                                 .build();
        }
        // Give the channel back so it can be acquired again by protocolImpl
        simpleChannelPool.release(newChannel);
        return protocolImpl;
    }

    @Override
    public Future release(Channel channel) {
        return release(channel, eventLoop.newPromise());
    }

    @Override
    public Future release(Channel channel, Promise promise) {
        doInEventLoop(eventLoop,
            () -> release0(channel, promise),
                      promise);
        return promise;
    }

    private void release0(Channel channel, Promise promise) {
        if (protocolImpl == null) {
            // If protocolImpl is null that means the first connection failed to establish. Release it back to the
            // underlying connection pool.
            simpleChannelPool.release(channel, promise);
        } else {
            protocolImpl.release(channel, promise);
        }
    }

    @Override
    public void close() {
        doInEventLoop(eventLoop, protocolImpl::close);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy