de.jackwhite20.comix.handler.UpstreamHandler.java Source code

Java tutorial

Introduction

Here is the source code for de.jackwhite20.comix.handler.UpstreamHandler.java

Source

/*
 * Copyright (c) 2015 "JackWhite20"
 *
 * This file is part of Comix.
 *
 * Comix is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.jackwhite20.comix.handler;

import de.jackwhite20.comix.Comix;
import de.jackwhite20.comix.network.ComixClient;
import de.jackwhite20.comix.strategy.BalancingStrategy;
import de.jackwhite20.comix.util.TargetData;
import de.jackwhite20.comix.util.Util;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

/**
 * Created by JackWhite20 on 17.07.2015.
 */
public class UpstreamHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private ComixClient client;

    private BalancingStrategy strategy;

    private Channel upstreamChannel;

    private Channel downstreamChannel;

    private boolean downstreamConnected;

    private DownstreamHandler downstreamHandler;

    private long upstreamBytesIn;

    private long downstreamBytesOut;

    private List<ByteBuf> initialPackets = new ArrayList<>();

    public UpstreamHandler(BalancingStrategy strategy) {
        this.strategy = strategy;
    }

    public void connectDownstream(ByteBuf initPacket) {
        InetSocketAddress address = (InetSocketAddress) upstreamChannel.remoteAddress();
        TargetData target = this.strategy.selectTarget(address.getHostName(), address.getPort());

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(upstreamChannel.eventLoop()).channel(upstreamChannel.getClass())
                .option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.AUTO_READ, false)
                .option(ChannelOption.SO_TIMEOUT, 5000).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                .handler(downstreamHandler = new DownstreamHandler(client, upstreamChannel));

        ChannelFuture f = bootstrap.connect(target.getHost(), target.getPort());
        downstreamChannel = f.channel();

        initialPackets.add(initPacket);

        f.addListener((future) -> {
            if (future.isSuccess()) {
                downstreamConnected = true;

                for (ByteBuf packet : initialPackets) {
                    downstreamChannel.writeAndFlush(packet);
                }

                Comix.getLogger().log(Level.INFO, "Proxy",
                        "[" + client.getName() + "] <-> [Comix] <-> [" + target.getName() + "] tunneled");
            } else {
                upstreamChannel.close();
            }
        });
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        upstreamChannel = ctx.channel();

        upstreamChannel.read();
    }

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
        if (downstreamConnected) {
            downstreamChannel.writeAndFlush(byteBuf.retain()).addListener((future) -> {
                if (future.isSuccess()) {
                    ctx.channel().read();
                } else {
                    ctx.channel().close();
                }
            });
        } else {
            ctx.channel().read();
        }

        upstreamBytesIn += byteBuf.readableBytes();
        downstreamBytesOut += byteBuf.readableBytes();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (downstreamChannel != null) {
            if (downstreamChannel.isActive()) {
                downstreamChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            }

            if (client != null)
                Comix.getInstance().removeClient(client);

            upstreamBytesIn = 0;
            downstreamBytesOut = 0;

            Comix.getLogger()
                    .info("["
                            + ((client != null) ? client.getName()
                                    : Util.formatSocketAddress(upstreamChannel.remoteAddress()))
                            + "] -> UpstreamHandler has disconnected");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Channel ch = ctx.channel();
        if (ch.isActive()) {
            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

    public void addInitialPacket(ByteBuf buf) {
        if (!downstreamConnected)
            initialPackets.add(buf);
    }

    public Channel getUpstreamChannel() {
        return upstreamChannel;
    }

    public DownstreamHandler getDownstreamHandler() {
        return downstreamHandler;
    }

    public void setClient(ComixClient client) {
        this.client = client;
    }

    public long getUpstreamBytesIn() {
        return upstreamBytesIn;
    }

    public long getDownstreamBytesOut() {
        return downstreamBytesOut;
    }

    public boolean isDownstreamConnected() {
        return downstreamConnected;
    }
}