com.tc.websocket.server.handler.ProxyFrontendHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.tc.websocket.server.handler.ProxyFrontendHandler.java

Source

/*
 * Original Work: Copyright 2012 The Netty Project
 * 
 * Modified Work: Copyright 2016 Tek Counsel LLC
 *
 * Both licenses this file to you 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.tc.websocket.server.handler;

import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.CertificateEncodingException;

import javax.security.cert.X509Certificate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;

import org.apache.commons.io.FileUtils;

import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.tc.di.guicer.IGuicer;
import com.tc.utils.StrUtils;
import com.tc.utils.StringCache;
import com.tc.websocket.Config;
import com.tc.websocket.Const;
import com.tc.websocket.server.pipeline.IPipelineBuilder;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslHandler;

// TODO: Auto-generated Javadoc
/**
 * The Class ProxyFrontendHandler.
 */
public class ProxyFrontendHandler extends ChannelInboundHandlerAdapter {

    /** The Constant LOG. */
    private static final Logger LOG = Logger.getLogger(ProxyFrontendHandler.class.getName());

    /** The remote host. */
    private final String remoteHost;

    /** The remote port. */
    private final int remotePort;

    /** The outbound channel. */
    private volatile Channel outboundChannel;

    /** The handler. */
    private ProxyBackendHandler handler;

    /** The builder. */
    @Inject
    @Named(Const.GUICE_WEBSOCKET_PIPELINE)
    private IPipelineBuilder builder;

    /** The guicer. */
    @Inject
    IGuicer guicer;

    /**
     * Instantiates a new proxy frontend handler.
     *
     * @param remoteHost the remote host
     * @param remotePort the remote port
     */
    public ProxyFrontendHandler(String remoteHost, int remotePort) {
        this.remoteHost = remoteHost;
        this.remotePort = remotePort;
    }

    /* (non-Javadoc)
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        final Channel inboundChannel = ctx.channel();

        this.handler = new ProxyBackendHandler(inboundChannel);

        // Start the connection attempt.
        Bootstrap b = new Bootstrap();
        b.group(inboundChannel.eventLoop()).channel(ctx.channel().getClass()).handler(this.handler)
                .option(ChannelOption.AUTO_READ, false);

        ChannelFuture f = b.connect(remoteHost, remotePort);
        outboundChannel = f.channel();

        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                if (future.isSuccess()) {
                    // connection complete start to read first data
                    inboundChannel.read();

                } else {
                    // Close the connection if the connection attempt has failed.
                    inboundChannel.close();
                }
            }
        });

    }

    /** The proxy. */
    private AtomicBoolean proxy = new AtomicBoolean(true);

    /* (non-Javadoc)
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
     */
    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) {

        ByteBuf buf = (ByteBuf) msg;
        String data = new String(ByteBufUtil.getBytes(buf));

        if (data.contains(Const.UPGRADE_WEBSOCKET) || data.contains(Const.GET_WEBSOCKET)) {
            proxy.set(false);

        } else if (data.contains(StringCache.HTTP_1_1)) {
            proxy.set(true);
        }

        if (proxy.get() == false) {
            writeToFile("frontend." + ctx.channel().id(), ByteBufUtil.getBytes(buf));
        }

        if (Config.getInstance().isCertAuth()) {
            SslHandler sslhandler = (SslHandler) ctx.channel().pipeline().get("ssl");
            try {
                X509Certificate cert = sslhandler.engine().getSession().getPeerCertificateChain()[0];
                Principal p = cert.getSubjectDN();

                /* Added by Miguel */
                LdapName ldapDN = new LdapName(p.getName());
                String username = "";
                String thumbprint = getThumbPrint(cert.getEncoded());

                for (Rdn rdn : ldapDN.getRdns()) {
                    //System.out.println(rdn.getType() + " -> " + rdn.getValue());
                    if (rdn.getType().equals("CN")) {
                        username = rdn.getValue().toString();
                        break;
                    }
                }
                /* End Added by Miguel*/

                String sessionId = parseSessionID(data);

                if (sessionId != null) {
                    String current = System.getProperty("user.dir");
                    //System.out.println("Current working directory in Java : " + current);

                    //File sessionFile = new File("c:/sessions/" + sessionId + ".txt");
                    File sessionFile = new File(current + "/data/sessions/" + sessionId + ".txt");

                    //only write the file if it hasn't been written yet.
                    if (sessionFile.createNewFile()) {
                        FileUtils.write(sessionFile, p.getName().replaceAll("\"", "").replaceAll("\\+", ",") + "\n"
                                + username + "\n" + thumbprint);
                    }
                }

            } catch (Exception e) {
                LOG.log(Level.SEVERE, null, e);
            }
        }

        if (proxy.get()) {
            ctx.channel().config().setAutoRead(false);
            if (outboundChannel.isActive()) {
                outboundChannel.writeAndFlush(buf).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) {
                        if (future.isSuccess()) {
                            // was able to flush out data, start to read the next chunk
                            ctx.channel().read();
                        } else {
                            future.channel().close();
                        }
                    }
                });
            }
        } else {
            //make sure the backend handler knows its a websocket connection.
            this.handler.setWebsocket(true);

            //get handle on the pipeline.
            ChannelPipeline pipeline = ctx.pipeline();

            //apply the websocket handlers
            builder.apply(pipeline);

            //remove this handler.
            pipeline.remove(this);

            //fire the event to move on to the next handler.
            ctx.fireChannelRead(msg);
        }

    }

    public static String getThumbPrint(byte[] der) throws NoSuchAlgorithmException, CertificateEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(der);
        byte[] digest = md.digest();
        return hexify(digest);

    }

    public static String hexify(byte bytes[]) {

        char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

        StringBuffer buf = new StringBuffer(bytes.length * 2);

        for (int i = 0; i < bytes.length; ++i) {
            buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
            buf.append(hexDigits[bytes[i] & 0x0f]);
        }

        return buf.toString();
    }

    /**
     * Write to file.
     *
     * @param prefix the prefix
     * @param bytes the bytes
     */
    public static void writeToFile(String prefix, byte[] bytes) {
        /*
        if(Config.getInstance().isDebug()){
           OutputStream out = null;
           try {
        out = new FileOutputStream("c:/temp/" + prefix + "-" + new Date().getTime() + ".txt");
        out.write(bytes);
        out.close();
            
           } catch (Exception e) {
        LOG.log(Level.SEVERE, null,e);
           }finally{
        IOUtils.closeQuietly(out);
           }
        }
         */
    }

    /* (non-Javadoc)
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelInactive(io.netty.channel.ChannelHandlerContext)
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (outboundChannel != null) {
            closeOnFlush(outboundChannel);
        }
    }

    /* (non-Javadoc)
     * @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause.getMessage().startsWith(Const.ESTABLISHED_CONN_ERR)) {
            LOG.log(Level.FINE, null, cause);
        } else {
            LOG.log(Level.SEVERE, null, cause);
        }
        closeOnFlush(ctx.channel());
    }

    /**
     * Closes the specified channel after all queued write requests are flushed.
     *
     * @param ch the ch
     */
    static void closeOnFlush(Channel ch) {
        if (ch.isActive()) {
            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

    private static String parseSessionID(String data) {
        //return StrUtils.parseCookie(data).get("SessionID");
        String[] req = data.split("\r\n");
        Map<String, String> cookies = new HashMap<String, String>();

        for (String str : req) {
            if (str.toLowerCase().contains("cookie")) {
                str = str.substring(str.indexOf(":") + 1, str.length());
                String[] pairs = str.split(";");
                for (String nv : pairs) {
                    String[] nameValue = nv.trim().split("=");
                    cookies.put(nameValue[0], nameValue[1]);
                }
            }
        }
        return cookies.get("SessionID");
    }

}