divconq.bus.net.StreamSession.java Source code

Java tutorial

Introduction

Here is the source code for divconq.bus.net.StreamSession.java

Source

/* ************************************************************************
#
#  DivConq
#
#  http://divconq.com/
#
#  Copyright:
#    Copyright 2014 eTimeline, LLC. All rights reserved.
#
#  License:
#    See the license.txt file in the project's top-level directory for details.
#
#  Authors:
#    * Andy White
#
************************************************************************ */
package divconq.bus.net;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import divconq.net.ssl.SslHandler;
import divconq.bus.HubRouter;
import divconq.hub.Hub;
import divconq.lang.op.OperationContext;
import divconq.log.Logger;
import divconq.util.KeyUtil;
import divconq.util.StringUtil;

public class StreamSession {
    protected Channel chan = null;
    protected HubRouter router = null;
    protected SocketInfo info = null;
    protected boolean isServerConnector = false;
    protected String mode = null;

    protected long written = 0;
    protected long read = 0;

    public long getWritten() {
        return this.written;
    }

    public long getRead() {
        return this.read;
    }

    public SocketInfo getSocketInfo() {
        return this.info;
    }

    public Channel getChannel() {
        return this.chan;
    }

    public void setChannel(Channel chan) {
        this.chan = chan;
    }

    public HubRouter getRouter() {
        return this.router;
    }

    public void setRouter(HubRouter router) {
        this.router = router;
    }

    public boolean isServerConnector() {
        return this.isServerConnector;
    }

    public String getSessionMode() {
        return this.mode;
    }

    public StreamSession(SocketInfo info, boolean isServer) {
        this.info = info;
        this.isServerConnector = isServer;
        this.mode = isServer ? "Server" : "Client";
    }

    public void close() {
        try {
            if (this.chan != null)
                this.chan.close().await(2000);
        } catch (InterruptedException x) {
            // ignore 
        }
    }

    // should already have TaskContext if needed (SERVICES and HELLO do not need task context)
    public boolean write(StreamMessage m) {
        try {
            if (this.chan != null) {
                Logger.trace("Stream session " + this + " sending message : " + m);

                //m.retain();
                ChannelFuture cf = this.chan.writeAndFlush(m).sync(); // we overrun the queue if we don't sync
                Logger.trace("Stream session " + this + " sent message : " + m);

                // record how much we wrote to this channel
                if (cf.isSuccess() && (m.getData() != null))
                    this.written += m.getData().writerIndex();

                // if we get here there is a decent chance the message was sent (no proof, just good chance)
                return true;
            }
        } catch (Exception x) {
            Logger.error("Error writing stream message: " + m);
            Logger.error("Error writing stream message: " + x);

            x.printStackTrace();

            // TODO close channel ?
        } finally {
            //m.release();      // if the data is on the network we don't need the buffer anymore  -- release in stream encoder
        }

        Logger.error("Could not write stream message");

        return false;
    }

    public void keepAlive() {
        try {
            if (this.chan != null) {
                Logger.trace("Stream session keep alive");
                StreamMessage m = Hub.instance.getBus().getLocalHub().buildStreamHello(OperationContext.getHubId());
                this.chan.writeAndFlush(m);
            }
        } catch (Exception x) {
            System.out.println("Error writing keep alive stream message: " + x);
            // TODO close channel ?
        }
    }

    public String getAttribute(String string) {
        // TODO Auto-generated method stub
        return null;
    }

    public void closed() {
        if (this.router != null)
            this.router.removeSession(this);
    }

    public boolean isInitialized() {
        return (this.router != null);
    }

    // TODO from this point on StreamMessage needs to be released 
    public void receiveMessage(StreamSession session, Channel ch, StreamMessage msg) {
        OperationContext.useHubContext();

        Logger.trace("Stream session " + this + " got message : " + msg);

        // record how much we read from this channel
        if (msg.getData() != null)
            this.read += msg.getData().writerIndex();

        if ("HELLO".equals(msg.getFieldAsString("Op"))) {
            String rhid = msg.getFieldAsString("Id");

            if (OperationContext.getHubId().equals(rhid)) {
                System.out.println(
                        "dcBus stream " + this.getSessionMode() + " tried to connect to self, got: " + msg);
                msg.release();
                ch.close(); // don't stay with bad messages
                return;
            }

            String expectedhubid = this.info.getHubId();

            if (StringUtil.isNotEmpty(expectedhubid) && !expectedhubid.equals(rhid)) {
                System.out.println("dcBus stream " + this.getSessionMode() + " tried to connect to " + expectedhubid
                        + ", got: " + msg);
                msg.release();
                ch.close(); // don't stay with bad messages
                return;
            }

            if (StringUtil.isDataInteger(rhid) && (rhid.length() == 5)) {
                if (!this.isInitialized()) {
                    if (this.info.isUseSsl()) {
                        // TODO maybe check the getPeerCertificateChain to see if the hubid and cert match up

                        try {
                            // ensure that connection has a client cert - if so it has gone through trust manager so we don't 
                            // need to verify cert, just be sure there is one
                            // if client does not present a cert we could still get this far
                            SslHandler sh = (SslHandler) ch.pipeline().get("ssl");

                            // TODO store in member var
                            X509Certificate[] xcerts = sh.engine().getSession().getPeerCertificateChain();

                            // TODO log not sysout
                            for (X509Certificate xc : xcerts)
                                System.out.println("confirmed cert is present: " + xc.getSubjectDN());

                            if (StringUtil.isNotEmpty(this.info.getTargetthumbprint())) {
                                String expected = this.info.getTargetthumbprint();
                                String got = KeyUtil.getCertThumbprint(xcerts[0]);

                                if (!expected.equals(got))
                                    throw new SSLPeerUnverifiedException(
                                            "Certificate does not match expected thumbprint: " + got);
                            }
                        } catch (SSLPeerUnverifiedException x) {
                            // TODO log, count, raise EVENT
                            System.err.println(
                                    "Peer Cert Error connecting dcBus " + this.getSessionMode() + ": " + x);
                            ch.close(); // don't stay with bad messages
                            return;
                        }
                    }

                    this.chan = ch;

                    this.router = Hub.instance.getBus().allocateOrGetHub(rhid, session.getSocketInfo().isGateway());
                    this.router.addSession(this);

                    Logger.info("dcBus stream " + this.getSessionMode() + " Greeted!");

                    // only server replies to HELLO, client started it
                    if (this.isServerConnector) {
                        // only the "server" side responds with HELLO

                        // Send HELLO to server to initial sequence of identity and service indexing
                        System.out.println("dcBus stream " + this.getSessionMode() + " sending HELLO");

                        StreamMessage icmd = Hub.instance.getBus().getLocalHub().buildStreamHello(rhid);
                        this.write(icmd);
                    }
                }
            }
        }

        // only accept HELLO messages until we get a valid one
        if (!this.isInitialized()) {
            System.out.println("dcBus stream " + this.getSessionMode() + " expceted HELLO message, got: " + msg);
            ch.close(); // don't stay with bad messages
            msg.release();
            return;
        }

        this.router.receiveMessage(session, msg);
    }
}