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

io.github.yawenok.apns.http2.impl.initializer.Http2ClientInitializer Maven / Gradle / Ivy

package io.github.yawenok.apns.http2.impl.initializer;

import io.github.yawenok.apns.http2.impl.handler.HttpResponseHandler;
import io.github.yawenok.apns.http2.impl.model.ResponseFuture;
import io.github.yawenok.apns.http2.config.ProxyConfig;
import io.github.yawenok.apns.http2.impl.NettyHelper;
import io.github.yawenok.apns.http2.impl.handler.Http2PingHandler;
import io.github.yawenok.apns.http2.impl.handler.Http2SettingsHandler;
import io.github.yawenok.apns.http2.impl.model.ResponseFutureCallback;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http2.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.proxy.ProxyHandler;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

public class Http2ClientInitializer extends ChannelInitializer {
    private SslContext sslContext;
    private ProxyConfig proxyConfig;

    private HttpResponseHandler responseHandler = new HttpResponseHandler();
    private Http2SettingsHandler settingsHandler;

    public Http2ClientInitializer(SslContext sslContext, ProxyConfig proxyConfig) {
        if (sslContext == null) {
            throw new IllegalArgumentException("sslContext can not be null!");
        }

        this.sslContext = sslContext;
        this.proxyConfig = proxyConfig;
    }

    public ResponseFuture putResponseFuture(final String apnsId, final ResponseFuture responseFuture) {
        return responseHandler.putResponseFuture(apnsId, responseFuture);
    }

    public ResponseFutureCallback putResponseFutureCallback(final String apnsId, final ResponseFutureCallback responseFutureCallback) {
        return responseHandler.putResponseFutureCallback(apnsId, responseFutureCallback);
    }

    public Object cleanApns(final String apnsId) {
        return responseHandler.cleanApns(apnsId);
    }

    public void awaitSettings(final long timeout, final TimeUnit unit) {
        settingsHandler.awaitSettings(timeout, unit);
    }

    @Override
    public void initChannel(final SocketChannel channel) throws Exception {
        ProxyHandler proxyHandler = null;
        if (proxyConfig != null) {
            proxyHandler = NettyHelper.getProxyHandler(proxyConfig);
        }
        settingsHandler = new Http2SettingsHandler(channel.newPromise());

        // 将Http2转化为Http1协议进行解析
        Http2Connection connection = new DefaultHttp2Connection(false);
        InboundHttp2ToHttpAdapter adapter = new InboundHttp2ToHttpAdapterBuilder(connection).maxContentLength(Integer.MAX_VALUE).propagateSettings(true).build();

        HttpToHttp2ConnectionHandlerBuilder builder = new HttpToHttp2ConnectionHandlerBuilder();
        builder.frameListener(new DelegatingDecompressorFrameListener(connection, adapter));
        builder.frameLogger(new Http2FrameLogger(LogLevel.TRACE, "Apns Http2 Logger"));

        HttpToHttp2ConnectionHandler connectionHandler = builder.connection(connection).build();
        Http2PingHandler pingHandler = new Http2PingHandler(connectionHandler.encoder());

        configureSSL(sslContext, channel, proxyHandler, connectionHandler, settingsHandler, pingHandler, responseHandler);
    }

    /**
     * Configure the pipeline for TLS NPN negotiation to HTTP/2.
     */
    private void configureSSL(final SslContext sslContext,
                              final SocketChannel channel,
                              final ProxyHandler proxyHandler,
                              final HttpToHttp2ConnectionHandler connectionHandler,
                              final Http2SettingsHandler settingsHandler,
                              final Http2PingHandler pingHandler,
                              final HttpResponseHandler responseHandler) {
        ChannelPipeline pipeline = channel.pipeline();
        if (proxyHandler != null) {
            pipeline.addFirst(proxyHandler);
        }

        pipeline.addLast(sslContext.newHandler(channel.alloc()));
        pipeline.addLast(new ProtocolNegotiationHandler("", connectionHandler, settingsHandler, pingHandler, responseHandler));

        // Http2 can keep a connection
        pipeline.addLast(new IdleStateHandler(0, pingHandler.getMaxIdle(), 0));
        pipeline.addLast(new IdleUserHandler());
    }

    /**
     * We must wait for the handshake to finish and the protocol to be negotiated before configuring the HTTP/2 components of the pipeline.
     */
    private final class ProtocolNegotiationHandler extends ApplicationProtocolNegotiationHandler {
        private final HttpToHttp2ConnectionHandler connectionHandler;
        private final Http2SettingsHandler settingsHandler;
        private final Http2PingHandler pingHandler;
        private final HttpResponseHandler responseHandler;

        /**
         * Creates a new instance with the specified fallback protocol name.
         *
         * @param fallbackProtocol the name of the protocol to use when
         *                         ALPN/NPN negotiation fails or the client does not support ALPN/NPN
         */
        public ProtocolNegotiationHandler(String fallbackProtocol,
                                          HttpToHttp2ConnectionHandler connectionHandler,
                                          Http2SettingsHandler settingsHandler,
                                          Http2PingHandler pingHandler,
                                          HttpResponseHandler responseHandler) {
            super(fallbackProtocol);

            this.connectionHandler = connectionHandler;
            this.settingsHandler = settingsHandler;
            this.pingHandler = pingHandler;
            this.responseHandler = responseHandler;
        }


        @Override
        protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                ChannelPipeline pipeline = ctx.pipeline();
                pipeline.addLast(connectionHandler);
                pipeline.addLast(settingsHandler, pingHandler, responseHandler);
                return;
            }

            ctx.close();
            throw new IllegalStateException("Unknown protocol: " + protocol);
        }
    }

    /**
     * Handle idle event
     */
    private final class IdleUserHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

            super.userEventTriggered(ctx, evt);
            if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
                IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
                if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
                    // 发送Ping消息
                    ctx.channel().write(Http2PingHandler.PING);
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy