package com.alibaba.doris.dataserver.net.protocol.text; import org.jboss.netty.buffer.ChannelBuffer; import com.alibaba.doris.common.data.util.ByteUtils; import com.alibaba.doris.dataserver.action.ActionFactory; import com.alibaba.doris.dataserver.action.data.ActionData; import com.alibaba.doris.dataserver.action.data.SupportBodyActionData; import com.alibaba.doris.dataserver.action.parser.ActionParser; import com.alibaba.doris.dataserver.net.InvalidCommandException; /** * @author ajun Email:jack.yuj@alibaba-inc.com */ public class TextProtocolDecoder { public ActionData readHeader(ChannelBuffer buf) { byte[] byteArray = readLine(buf); if (byteArray != null) { return parseHeader(byteArray); } return null; } /** * 从数据流中读取一行数据,行分割符:\r\n * * @param buf * @return */ public byte[] readLine(ChannelBuffer buf) { byte b = 0; int pos; int startPos = pos = buf.readerIndex(); int limit = buf.writerIndex(); boolean eol = false; byte[] array = buf.array(); //buf.markReaderIndex(); while (pos < limit) { b = array[pos++]; if (b == ProtocolConstant.CRLF[0]) { eol = true; } else { if (eol) { if (b == ProtocolConstant.CRLF[1]) { eol = false; byte[] byteArray = new byte[pos - startPos]; // buf.resetReaderIndex(); buf.readBytes(byteArray); return byteArray; } eol = false; } } } // buf.resetReaderIndex(); return null; } /** * 跳过流中的一段数据,以\r\n作为段分割符,返回true:表示成功跳过流中的一段数据,跳过的数据段<br> * 将无法被读取。如果返回false:标识流中的数据不完整,数据的指针会回到<br> * 读取前的位置。 * * @param buf * @return */ public boolean skipLine(ChannelBuffer buf) { byte b = 0; int pos = buf.readerIndex(); int limit = buf.readableBytes(); boolean eol = false; buf.markReaderIndex(); while (pos < limit) { b = buf.readByte(); pos++; if (b == ProtocolConstant.CRLF[0]) { eol = true; } else { if (eol) { if (b == ProtocolConstant.CRLF[1]) { eol = false; return true; } eol = false; } } } buf.resetReaderIndex(); return false; } public byte[] readBody(ChannelBuffer buf, SupportBodyActionData md) { int bodyBytes = md.getBodyBytes(); if (bodyBytes > MAX_BODY_SIZE) { throw new InvalidCommandException("Invalid command body, The body is too large."); } if (buf.readableBytes() >= (bodyBytes + ProtocolConstant.CRLF.length)) { byte[] bodyArray = new byte[bodyBytes]; buf.readBytes(bodyArray); // check body length byte cr = buf.readByte(); byte lf = buf.readByte(); if (cr != ProtocolConstant.CRLF[0] || lf != ProtocolConstant.CRLF[1]) { throw new InvalidCommandException("Invalid body data, The command body length is " + md.getBodyBytes() + "."); } return bodyArray; } else { return null; } } public ActionData parseHeader(byte[] header) { int pos = 0; // skip all space of the command head. while (header[pos] == ProtocolConstant.SPACE) { pos++; } int startPos = pos; while (header[pos] != ProtocolConstant.SPACE && header[pos] != ProtocolConstant.CRLF[0]) { pos++; } String commandName = ByteUtils.byteToString(header, startPos, pos - startPos); ActionParser parser = ActionFactory.getActionParser(commandName); if (null != parser) { // skip ' ' if (header[pos] == ProtocolConstant.SPACE) { pos++; } return parser.readHead(header, pos); } throw new InvalidCommandException("Unknown command : '" + commandName + "'"); } protected String parseNextField(byte[] line, int[] startPos) { int pos = startPos[0]; while (line[pos] != ProtocolConstant.SPACE && line[pos] != ProtocolConstant.CRLF[0]) { pos++; } // skip ' ' pos++; String value = ByteUtils.byteToString(line, startPos[0], (pos - startPos[0] - 1)); startPos[0] = pos; return value; } /** * 为了提升性能,new String会消耗大量CPU资源。 * * @param line * @param startPos */ protected void skipNextField(byte[] line, int[] startPos) { int pos = startPos[0]; while (line[pos] != ProtocolConstant.SPACE && line[pos] != ProtocolConstant.CRLF[0]) { pos++; } // skip ' ' pos++; startPos[0] = pos; } private static final int MAX_BODY_SIZE = 1024 * 1024; // The max lenght of body. }