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

com.hivemq.client.internal.mqtt.handler.proxy.MqttProxyAdapterHandler Maven / Gradle / Ivy

Go to download

HiveMQ MQTT Client is an MQTT 5.0 and MQTT 3.1.1 compatible and feature-rich high-performance Java client library with different API flavours and backpressure support

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright 2018-present HiveMQ and the HiveMQ Community
 *
 * 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.client.internal.mqtt.handler.proxy;

import com.hivemq.client.internal.mqtt.MqttProxyConfigImpl;
import io.netty.channel.*;
import io.netty.handler.proxy.HttpProxyHandler;
import io.netty.handler.proxy.ProxyHandler;
import io.netty.handler.proxy.Socks4ProxyHandler;
import io.netty.handler.proxy.Socks5ProxyHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.NoSuchElementException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author Silvio Giebl
 */
class MqttProxyAdapterHandler extends ChannelDuplexHandler implements FutureListener {

    public static final @NotNull String NAME = "proxy.adapter";
    private static final @NotNull String PROXY_HANDLER_NAME = "proxy";

    private final @NotNull MqttProxyConfigImpl proxyConfig;
    private final @NotNull InetSocketAddress serverAddress;
    private final @NotNull Consumer onSuccess;
    private final @NotNull BiConsumer onError;
    private boolean handshakeDone = false;

    public MqttProxyAdapterHandler(
            final @NotNull MqttProxyConfigImpl proxyConfig,
            final @NotNull InetSocketAddress serverAddress,
            final @NotNull Consumer onSuccess,
            final @NotNull BiConsumer onError) {

        this.proxyConfig = proxyConfig;
        this.serverAddress = serverAddress;
        this.onSuccess = onSuccess;
        this.onError = onError;
    }

    @Override
    public void connect(
            final @NotNull ChannelHandlerContext ctx,
            final @NotNull SocketAddress remoteAddress,
            final @Nullable SocketAddress localAddress,
            final @NotNull ChannelPromise promise) {

        final Channel channel = ctx.channel();
        final String username = proxyConfig.getRawUsername();
        final String password = proxyConfig.getRawPassword();

        final ProxyHandler proxyHandler;
        switch (proxyConfig.getProtocol()) {
            case SOCKS_4:
                proxyHandler = new Socks4ProxyHandler(remoteAddress, username);
                break;
            case SOCKS_5:
                proxyHandler = new Socks5ProxyHandler(remoteAddress, username, password);
                break;
            case HTTP:
                if ((username == null) && (password == null)) {
                    proxyHandler = new HttpProxyHandler(remoteAddress);
                } else {
                    proxyHandler = new HttpProxyHandler(remoteAddress, (username == null) ? "" : username,
                            (password == null) ? "" : password);
                }
                break;
            default:
                if (setHandshakeDone(channel.pipeline())) {
                    onError.accept(
                            channel, new IllegalStateException("Unknown proxy protocol " + proxyConfig.getProtocol()));
                }
                return;
        }

        proxyHandler.setConnectTimeoutMillis(proxyConfig.getHandshakeTimeoutMs());

        proxyHandler.connectFuture().addListener(this);

        channel.pipeline().addFirst(PROXY_HANDLER_NAME, proxyHandler);

        ctx.connect(serverAddress, localAddress, promise);
    }

    @Override
    public void operationComplete(final @NotNull Future future) {
        if (future.isSuccess()) {
            final Channel channel = future.getNow();
            if (setHandshakeDone(channel.pipeline())) {
                onSuccess.accept(channel);
            }
        }
        // onError is handled in exceptionCaught because the exception is fired after the connect future is set and
        // otherwise "An exceptionCaught() event was fired, and it reached at the tail of the pipeline" would be logged
    }

    @Override
    public void exceptionCaught(final @NotNull ChannelHandlerContext ctx, final @NotNull Throwable cause) {
        if (setHandshakeDone(ctx.pipeline())) {
            onError.accept(ctx.channel(), cause);
        } else {
            ctx.fireExceptionCaught(cause);
        }
    }

    private boolean setHandshakeDone(final @NotNull ChannelPipeline pipeline) {
        if (!handshakeDone) {
            handshakeDone = true;
            pipeline.remove(this);
            try {
                pipeline.remove(PROXY_HANDLER_NAME);
            } catch (final NoSuchElementException ignored) {
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean isSharable() {
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy