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

com.hivemq.bootstrap.netty.initializer.AbstractChannelInitializer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019-present HiveMQ GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 com.hivemq.bootstrap.netty.initializer;

import com.google.common.base.Preconditions;
import com.hivemq.bootstrap.ClientConnectionContext;
import com.hivemq.bootstrap.UndefinedClientConnection;
import com.hivemq.bootstrap.netty.ChannelDependencies;
import com.hivemq.codec.decoder.MQTTMessageDecoder;
import com.hivemq.configuration.service.InternalConfigurations;
import com.hivemq.configuration.service.RestrictionsConfigurationService;
import com.hivemq.configuration.service.entity.Listener;
import com.hivemq.extension.sdk.api.annotations.NotNull;
import com.hivemq.mqtt.handler.connect.MessageBarrier;
import com.hivemq.mqtt.handler.publish.PublishFlushHandler;
import com.hivemq.security.exception.SslException;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

import static com.hivemq.bootstrap.netty.ChannelHandlerNames.*;
import static com.hivemq.logging.LoggingUtils.appendListenerToMessage;


public abstract class AbstractChannelInitializer extends ChannelInitializer {

    private static final @NotNull Logger log = LoggerFactory.getLogger(AbstractChannelInitializer.class);

    private final @NotNull ChannelDependencies channelDependencies;
    private final @NotNull Listener listener;
    private final boolean throttlingEnabled;
    private final boolean legacyNettyShutdown;

    protected AbstractChannelInitializer(
            final @NotNull ChannelDependencies channelDependencies, final @NotNull Listener listener) {
        this.channelDependencies = channelDependencies;
        this.listener = listener;
        final boolean incomingEnabled = channelDependencies.getRestrictionsConfigurationService().incomingLimit() > 0;
        final boolean outgoingEnabled = InternalConfigurations.OUTGOING_BANDWIDTH_THROTTLING_DEFAULT_BYTES_PER_SEC > 0;
        legacyNettyShutdown = InternalConfigurations.NETTY_SHUTDOWN_LEGACY;
        throttlingEnabled = incomingEnabled || outgoingEnabled;
    }

    @Override
    protected void initChannel(final @NotNull Channel ch) throws Exception {

        Preconditions.checkNotNull(ch, "Channel must never be null");

        if (!legacyNettyShutdown && channelDependencies.getShutdownHooks().isShuttingDown()) {
            //during shutting down, we dont want new clients to create any pipeline,
            //and we dont want to read from their socket
            ch.config().setAutoRead(false);
            ch.close();
            return;
        }

        final PublishFlushHandler publishFlushHandler = channelDependencies.createPublishFlushHandler();
        final UndefinedClientConnection clientContext =
                new UndefinedClientConnection(ch, publishFlushHandler, listener);
        ch.attr(ClientConnectionContext.CHANNEL_ATTRIBUTE_NAME).set(clientContext);

        ch.pipeline()
                .addLast(ALL_CHANNELS_GROUP_HANDLER, new ChannelGroupHandler(channelDependencies.getChannelGroup()));
        if (throttlingEnabled) {
            ch.pipeline().addLast(GLOBAL_THROTTLING_HANDLER, channelDependencies.getGlobalTrafficShapingHandler());
        }
        ch.pipeline().addLast(MQTT_MESSAGE_DECODER, new MQTTMessageDecoder(channelDependencies));
        ch.pipeline().addLast(MQTT_MESSAGE_ENCODER, channelDependencies.getMqttMessageEncoder());
        addNoConnectIdleHandler(ch);
        //MQTT_5_FLOW_CONTROL_HANDLER is added here after CONNECT
        ch.pipeline()
                .addLast(MQTT_MESSAGE_BARRIER, new MessageBarrier(channelDependencies.getMqttServerDisconnector()));
        // before connack outbound interceptor as it initializes the client context after the connack
        ch.pipeline().addLast(PLUGIN_INITIALIZER_HANDLER, channelDependencies.getPluginInitializerHandler());

        ch.pipeline().addLast(INTERCEPTOR_HANDLER, channelDependencies.getInterceptorHandler());

        //MQTT_PUBLISH_FLOW_HANDLER is added here after CONNECT
        ch.pipeline().addLast(MESSAGE_EXPIRY_HANDLER, channelDependencies.getPublishMessageExpiryHandler());

        ch.pipeline().addLast(MQTT_SUBSCRIBE_HANDLER, channelDependencies.getSubscribeHandler());

        ch.pipeline().addLast(PUBLISH_FLUSH_HANDLER, publishFlushHandler);
        // after connect inbound interceptor as it intercepts the connect
        ch.pipeline().addLast(CLIENT_LIFECYCLE_EVENT_HANDLER, channelDependencies.getClientLifecycleEventHandler());

        ch.pipeline().addLast(MQTT_AUTH_HANDLER, channelDependencies.getAuthHandler());
        ch.pipeline().addLast(CONNECTION_LIMITER, channelDependencies.getConnectionLimiterHandler());
        ch.pipeline().addLast(MQTT_CONNECT_HANDLER, channelDependencies.getConnectHandler());


        ch.pipeline().addLast(MQTT_PINGREQ_HANDLER, channelDependencies.getPingRequestHandler());
        ch.pipeline().addLast(MQTT_UNSUBSCRIBE_HANDLER, channelDependencies.getUnsubscribeHandler());
        ch.pipeline().addLast(MQTT_DISCONNECT_HANDLER, channelDependencies.getDisconnectHandler());

        addSpecialHandlers(ch);

        ch.pipeline().addLast(EXCEPTION_HANDLER, channelDependencies.getExceptionHandler());
    }

    protected void addNoConnectIdleHandler(final @NotNull Channel ch) {

        //get timeout value from internal config
        final RestrictionsConfigurationService restrictionsConfig =
                channelDependencies.getRestrictionsConfigurationService();

        final long timeoutMillis = restrictionsConfig.noConnectIdleTimeout();

        if (timeoutMillis > 0) {
            final IdleStateHandler idleStateHandler = new IdleStateHandler(timeoutMillis, 0, 0, TimeUnit.MILLISECONDS);

            ch.pipeline().addAfter(MQTT_MESSAGE_ENCODER, NEW_CONNECTION_IDLE_HANDLER, idleStateHandler);
            ch.pipeline()
                    .addAfter(NEW_CONNECTION_IDLE_HANDLER,
                            NO_CONNECT_IDLE_EVENT_HANDLER,
                            channelDependencies.getNoConnectIdleHandler());
        }
    }

    protected abstract void addSpecialHandlers(final @NotNull Channel ch) throws Exception;

    @Override
    public void exceptionCaught(final @NotNull ChannelHandlerContext ctx, final @NotNull Throwable cause)
            throws Exception {
        if (cause instanceof SslException) {
            final ClientConnectionContext clientConnectionContext = ClientConnectionContext.of(ctx.channel());
            log.error("{}. Disconnecting client {} ",
                    cause.getMessage(),
                    clientConnectionContext.getChannelIP().orElse("UNKNOWN"));
            log.debug("Original exception:", cause);
            final String eventLogMessage = appendListenerToMessage(ctx.channel(), cause.getMessage());
            //We need to close the channel because the initialization wasn't successful
            channelDependencies.getMqttServerDisconnector().logAndClose(ctx.channel(), null, //already logged
                    eventLogMessage);
        } else {
            //Just use the default handler
            super.exceptionCaught(ctx, cause);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy