io.vertx.core.http.impl.VertxHttp2ConnectionHandler.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.http.impl.VertxHttp2ConnectionHandler.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 *  ------------------------------------------------------
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *      The Eclipse Public License is available at
 *      http://www.eclipse.org/legal/epl-v10.html
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.core.http.impl;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.Http2Connection;
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
import io.netty.handler.codec.http2.Http2ConnectionHandler;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Flags;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2RemoteFlowController;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.Http2Stream;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.EventExecutor;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;

import java.util.Map;
import java.util.function.Function;

/**
 * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
 */
class VertxHttp2ConnectionHandler<C extends Http2ConnectionBase> extends Http2ConnectionHandler
        implements Http2Connection.Listener {

    private final Map<Channel, ? super C> connectionMap;
    C connection;
    private ChannelHandlerContext ctx;

    public VertxHttp2ConnectionHandler(Map<Channel, ? super C> connectionMap, Http2ConnectionDecoder decoder,
            Http2ConnectionEncoder encoder, Http2Settings initialSettings,
            Function<VertxHttp2ConnectionHandler<C>, C> connectionFactory) {
        super(decoder, encoder, initialSettings);
        this.connectionMap = connectionMap;
        this.connection = connectionFactory.apply(this);
        encoder().flowController().listener(s -> connection.onStreamwritabilityChanged(s));
        connection().addListener(this);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        super.handlerAdded(ctx);
        this.ctx = ctx;
        connection.setHandlerContext(ctx);
        connectionMap.put(ctx.channel(), connection);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        connectionMap.remove(ctx.channel());
        connection.getContext().executeFromIO(connection::handleClosed);
    }

    @Override
    protected void onConnectionError(ChannelHandlerContext ctx, Throwable cause, Http2Exception http2Ex) {
        connection.getContext().executeFromIO(() -> {
            connection.onConnectionError(cause);
        });
        // Default behavior send go away
        super.onConnectionError(ctx, cause, http2Ex);
    }

    @Override
    protected void onStreamError(ChannelHandlerContext ctx, Throwable cause,
            Http2Exception.StreamException http2Ex) {
        connection.getContext().executeFromIO(() -> {
            connection.onStreamError(http2Ex.streamId(), http2Ex);
        });
        // Default behavior reset stream
        super.onStreamError(ctx, cause, http2Ex);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        try {
            super.userEventTriggered(ctx, evt);
        } finally {
            if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) {
                ctx.close();
            }
        }
    }

    //

    @Override
    public void onStreamClosed(Http2Stream stream) {
        connection.onStreamClosed(stream);
    }

    @Override
    public void onStreamAdded(Http2Stream stream) {
    }

    @Override
    public void onStreamActive(Http2Stream stream) {
    }

    @Override
    public void onStreamHalfClosed(Http2Stream stream) {
    }

    @Override
    public void onStreamRemoved(Http2Stream stream) {
    }

    @Override
    public void onPriorityTreeParentChanged(Http2Stream stream, Http2Stream oldParent) {
    }

    @Override
    public void onPriorityTreeParentChanging(Http2Stream stream, Http2Stream newParent) {
    }

    @Override
    public void onWeightChanged(Http2Stream stream, short oldWeight) {
    }

    @Override
    public void onGoAwaySent(int lastStreamId, long errorCode, ByteBuf debugData) {
        connection.onGoAwaySent(lastStreamId, errorCode, debugData);
    }

    @Override
    public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) {
        connection.onGoAwayReceived(lastStreamId, errorCode, debugData);
    }

    //

    void writeHeaders(Http2Stream stream, Http2Headers headers, boolean end) {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeHeaders(stream, headers, end);
        } else {
            executor.execute(() -> {
                _writeHeaders(stream, headers, end);
            });
        }
    }

    private void _writeHeaders(Http2Stream stream, Http2Headers headers, boolean end) {
        encoder().writeHeaders(ctx, stream.id(), headers, 0, end, ctx.newPromise());
        ;
    }

    void writeData(Http2Stream stream, ByteBuf chunk, boolean end) {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeData(stream, chunk, end);
        } else {
            executor.execute(() -> {
                _writeData(stream, chunk, end);
            });
        }
    }

    private void _writeData(Http2Stream stream, ByteBuf chunk, boolean end) {
        encoder().writeData(ctx, stream.id(), chunk, 0, end, ctx.newPromise());
        Http2RemoteFlowController controller = encoder().flowController();
        if (!controller.isWritable(stream) || end) {
            try {
                encoder().flowController().writePendingBytes();
            } catch (Http2Exception e) {
                onError(ctx, e);
            }
        }
        ctx.channel().flush();
    }

    ChannelFuture writePing(ByteBuf data) {
        ChannelPromise promise = ctx.newPromise();
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writePing(data, promise);
        } else {
            executor.execute(() -> {
                _writePing(data, promise);
            });
        }
        return promise;
    }

    private void _writePing(ByteBuf data, ChannelPromise promise) {
        encoder().writePing(ctx, false, data, promise);
        ctx.channel().flush();
    }

    /**
     * Consume {@code numBytes} for {@code stream}  in the flow controller, this must be called from event loop.
     */
    void consume(Http2Stream stream, int numBytes) {
        try {
            boolean windowUpdateSent = decoder().flowController().consumeBytes(stream, numBytes);
            if (windowUpdateSent) {
                ctx.channel().flush();
            }
        } catch (Http2Exception e) {
            onError(ctx, e);
        }
    }

    void writeFrame(Http2Stream stream, byte type, short flags, ByteBuf payload) {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeFrame(stream, type, flags, payload);
        } else {
            executor.execute(() -> {
                _writeFrame(stream, type, flags, payload);
            });
        }
    }

    private void _writeFrame(Http2Stream stream, byte type, short flags, ByteBuf payload) {
        encoder().writeFrame(ctx, type, stream.id(), new Http2Flags(flags), payload, ctx.newPromise());
        ctx.flush();
    }

    void writeReset(int streamId, long code) {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeReset(streamId, code);
        } else {
            executor.execute(() -> {
                _writeReset(streamId, code);
            });
        }
    }

    private void _writeReset(int streamId, long code) {
        encoder().writeRstStream(ctx, streamId, code, ctx.newPromise());
        ctx.flush();
    }

    void writeGoAway(long errorCode, int lastStreamId, ByteBuf debugData) {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeGoAway(errorCode, lastStreamId, debugData);
        } else {
            executor.execute(() -> {
                _writeGoAway(errorCode, lastStreamId, debugData);
            });
        }
    }

    private void _writeGoAway(long errorCode, int lastStreamId, ByteBuf debugData) {
        encoder().writeGoAway(ctx, lastStreamId, errorCode, debugData, ctx.newPromise());
        ctx.flush();
    }

    ChannelFuture writeSettings(Http2Settings settingsUpdate) {
        ChannelPromise promise = ctx.newPromise();
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writeSettings(settingsUpdate, promise);
        } else {
            executor.execute(() -> {
                _writeSettings(settingsUpdate, promise);
            });
        }
        return promise;
    }

    private void _writeSettings(Http2Settings settingsUpdate, ChannelPromise promise) {
        encoder().writeSettings(ctx, settingsUpdate, promise);
        ctx.flush();
    }

    void writePushPromise(int streamId, Http2Headers headers, Handler<AsyncResult<Integer>> completionHandler) {
        int promisedStreamId = connection().local().incrementAndGetNextStreamId();
        ChannelPromise promise = ctx.newPromise();
        promise.addListener(fut -> {
            if (fut.isSuccess()) {
                completionHandler.handle(Future.succeededFuture(promisedStreamId));
            } else {
                completionHandler.handle(Future.failedFuture(fut.cause()));
            }
        });
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            _writePushPromise(streamId, promisedStreamId, headers, promise);
        } else {
            executor.execute(() -> {
                _writePushPromise(streamId, promisedStreamId, headers, promise);
            });
        }
    }

    private void _writePushPromise(int streamId, int promisedStreamId, Http2Headers headers,
            ChannelPromise promise) {
        encoder().writePushPromise(ctx, streamId, promisedStreamId, headers, 0, promise);
    }
}