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

com.feingto.iot.server.codec.RouteChannelDecoder Maven / Gradle / Ivy

There is a newer version: 1.2.5.RELEASE
Show newest version
package com.feingto.iot.server.codec;

import com.feingto.iot.common.bootstrap.SimpleHandlerLoader;
import com.feingto.iot.server.handler.HttpRequestHandler;
import com.feingto.iot.server.handler.TcpMessageHandler;
import com.feingto.iot.server.handler.WebSocketMessageHandler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.AppendableCharSequence;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 路由通道解码器, 支持单口双工协议 TCP 和 WebSocket
 *
 * @author longfei
 * @see io.netty.handler.codec.http.HttpObjectDecoder
 */
public class RouteChannelDecoder extends ByteToMessageDecoder {
    private final LineParser lineParser;

    public RouteChannelDecoder() {
        this.lineParser = new LineParser(new AppendableCharSequence(128), 4096);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {
        List channelHandlers;
        if (skipControlCharacters(in)) {
            channelHandlers = SimpleHandlerLoader.getHttpChannelHandlers();
            channelHandlers.add(new WebSocketMessageHandler());
            channelHandlers.add(new HttpRequestHandler());
        } else {
            channelHandlers = SimpleHandlerLoader.getTcpHandlers();
            channelHandlers.add(new IdleStateHandler(2, 0, 0, TimeUnit.MINUTES));
            channelHandlers.add(new TcpMessageHandler());
        }
        channelHandlers.forEach(handler -> ctx.pipeline().addLast(handler));
        ctx.pipeline().remove(this);
    }

    private boolean skipControlCharacters(ByteBuf buffer) {
        buffer.markReaderIndex();
        buffer.markWriterIndex();
        boolean skiped = false;
        final int wIdx = buffer.writerIndex();
        int rIdx = buffer.readerIndex();
        while (wIdx > rIdx) {
            int c = buffer.getUnsignedByte(rIdx++);
            if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
                rIdx--;
                skiped = true;
                break;
            }
        }
        if (skiped && lineParser.parse(buffer) == null) {
            skiped = false;
        }
        buffer.readerIndex(rIdx);
        buffer.resetReaderIndex();
        buffer.resetWriterIndex();
        return skiped;
    }

    private static class HeaderParser implements ByteProcessor {
        private final AppendableCharSequence seq;
        private final int maxLength;
        private int size;

        HeaderParser(AppendableCharSequence seq, int maxLength) {
            this.seq = seq;
            this.maxLength = maxLength;
        }

        public AppendableCharSequence parse(ByteBuf buffer) {
            final int oldSize = size;
            seq.reset();
            int i = buffer.forEachByte(this);
            if (i == -1) {
                size = oldSize;
                return null;
            }
            buffer.readerIndex(i + 1);
            return seq;
        }

        public void reset() {
            size = 0;
        }

        @Override
        public boolean process(byte value) {
            char nextByte = (char) (value & 0xFF);
            if (nextByte == HttpConstants.CR) {
                return true;
            }
            if (nextByte == HttpConstants.LF) {
                return false;
            }

            if (++size > maxLength) {
                throw newException(maxLength);
            }

            seq.append(nextByte);
            return true;
        }

        protected TooLongFrameException newException(int maxLength) {
            return new TooLongFrameException("HTTP header is larger than " + maxLength + " bytes.");
        }
    }

    private static final class LineParser extends HeaderParser {
        LineParser(AppendableCharSequence seq, int maxLength) {
            super(seq, maxLength);
        }

        @Override
        public AppendableCharSequence parse(ByteBuf buffer) {
            reset();
            return super.parse(buffer);
        }

        @Override
        protected TooLongFrameException newException(int maxLength) {
            return new TooLongFrameException("An HTTP line is larger than " + maxLength + " bytes.");
        }
    }
}