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

io.reactivex.mantis.network.push.LegacyTcpPipelineConfigurator Maven / Gradle / Ivy

/*
 * Copyright 2019 Netflix, Inc.
 *
 * 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 io.reactivex.mantis.network.push;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import mantis.io.reactivex.netty.pipeline.PipelineConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class LegacyTcpPipelineConfigurator implements PipelineConfigurator> {

    private static final Logger logger = LoggerFactory.getLogger(LegacyTcpPipelineConfigurator.class);
    private static final byte PROTOCOL_VERSION = 1;

    private String name;

    public LegacyTcpPipelineConfigurator(String name) {
        this.name = name;
    }

    @SuppressWarnings("unchecked")
    static Map fromBytesToMap(byte[] bytes) {
        Map map = null;
        ByteArrayInputStream bis = null;
        ObjectInput in = null;
        try {
            bis = new ByteArrayInputStream(bytes);
            in = new ObjectInputStream(bis);
            map = (Map) in.readObject();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException(e1);
        } finally {
            try {
                if (bis != null) {bis.close();}
                if (in != null) {in.close();}
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return map;
    }

    static byte[] fromMapToBytes(Map map) {
        ByteArrayOutputStream baos = null;
        ObjectOutput out = null;
        try {
            baos = new ByteArrayOutputStream();
            out = new ObjectOutputStream(baos);
            out.writeObject(map);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (out != null) {out.close();}
                if (baos != null) {baos.close();}
            } catch (IOException e1) {
                e1.printStackTrace();
                throw new RuntimeException(e1);
            }
        }
        return baos.toByteArray();
    }

    @Override
    public void configureNewPipeline(ChannelPipeline pipeline) {

        pipeline.addLast(new ChannelDuplexHandler() {

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg)
                    throws Exception {
                boolean handled = false;
                if (ByteBuf.class.isAssignableFrom(msg.getClass())) {


                    ByteBuf byteBuf = (ByteBuf) msg;
                    if (byteBuf.isReadable()) {
                        int protocolVersion = byteBuf.readByte();
                        if (protocolVersion != PROTOCOL_VERSION) {
                            throw new RuntimeException("Unsupported protocol version: " + protocolVersion);
                        }
                        int observableNameLength = byteBuf.readByte();
                        String observableName = null;
                        if (observableNameLength > 0) {
                            // read name
                            byte[] observableNameBytes = new byte[observableNameLength];
                            byteBuf.readBytes(observableNameBytes);
                            observableName = new String(observableNameBytes, Charset.forName("UTF-8"));
                        }

                        while (byteBuf.isReadable()) {
                            int lengthOfEvent = byteBuf.readInt();
                            int operation = byteBuf.readByte();
                            RemoteRxEvent.Type type = null;
                            Map subscribeParams = null;
                            byte[] valueData = null;
                            if (operation == 1) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: next");
                                }
                                type = RemoteRxEvent.Type.next;
                                valueData = new byte[lengthOfEvent - 1]; //subtract op code
                                byteBuf.readBytes(valueData);
                            } else if (operation == 2) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: error");
                                }
                                type = RemoteRxEvent.Type.error;
                                valueData = new byte[lengthOfEvent - 1];
                                byteBuf.readBytes(valueData);
                            } else if (operation == 3) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: completed");
                                }
                                type = RemoteRxEvent.Type.completed;
                            } else if (operation == 4) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: subscribed");
                                }
                                type = RemoteRxEvent.Type.subscribed;
                                // read subscribe parameters
                                int subscribeParamsLength = byteBuf.readInt();
                                if (subscribeParamsLength > 0) {
                                    // read byte into map
                                    byte[] subscribeParamsBytes = new byte[subscribeParamsLength];
                                    byteBuf.readBytes(subscribeParamsBytes);
                                    subscribeParams = fromBytesToMap(subscribeParamsBytes);
                                }
                            } else if (operation == 5) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: unsubscribed");
                                }
                                type = RemoteRxEvent.Type.unsubscribed;
                            } else if (operation == 6) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: heartbeat");
                                }
                            } else if (operation == 7) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("READ request for RemoteRxEvent: nonDataError");
                                }
                                type = RemoteRxEvent.Type.nonDataError;
                                valueData = new byte[lengthOfEvent - 1];
                                byteBuf.readBytes(valueData);
                            } else {
                                throw new RuntimeException("operation: " + operation + " not support.");
                            }
                            // don't send heartbeats through pipeline
                            if (operation != 6) {
                                ctx.fireChannelRead(new RemoteRxEvent(observableName, type, valueData, subscribeParams));
                            }
                        }
                        handled = true;
                        byteBuf.release();
                    }
                }
                if (!handled) {
                    super.channelRead(ctx, msg);
                }

            }
        });

        pipeline.addLast(new ChannelDuplexHandler() {
            @Override
            public void write(ChannelHandlerContext ctx, Object msg,
                              ChannelPromise promise) throws Exception {

                if (ByteBuf.class.isAssignableFrom(msg.getClass())) {
                    // handle data writes
                    ByteBuf bytes = (ByteBuf) msg;
                    ByteBuf buf = ctx.alloc().buffer(bytes.readableBytes());
                    writeHeader(buf, name);
                    buf.writeBytes(bytes);
                    bytes.release();
                    super.write(ctx, buf, promise);
                } else if (msg instanceof byte[]) {
                    // handle heart beat writes
                    ByteBuf buf = ctx.alloc().buffer();
                    writeHeader(buf, name);
                    buf.writeBytes((byte[]) msg);
                    super.write(ctx, buf, promise);
                    super.flush(ctx);
                } else {
                    super.write(ctx, msg, promise);
                }
            }
        });
    }

    private void writeHeader(ByteBuf buf, String name) {
        buf.writeByte(PROTOCOL_VERSION);
        String observableName = name;
        if (observableName != null && !observableName.isEmpty()) {
            // write length
            int nameLength = observableName.length();
            if (nameLength < 127) {
                buf.writeByte(nameLength);
                buf.writeBytes(observableName.getBytes());
            } else {
                throw new RuntimeException("observableName " + observableName +
                        " exceeds max limit of 127 characters");
            }
        } else {
            // no name provided, write 0 bytes for name length
            buf.writeByte(0);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy