/**
* Tencent is pleased to support the open source community by making MSEC available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the GNU General Public 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
*
* https://opensource.org/licenses/GPL-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 org.msec.net;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.buffer.DynamicChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
import org.msec.rpc.CustomPackageCodec;
import org.msec.rpc.CustomPackageLengthChecker;
import org.msec.rpc.RpcResponse;
import srpc.Head;
import java.util.ArrayList;
import java.util.List;
public class ResponseDecoder extends OneToOneDecoder {
private static Logger log = Logger.getLogger(ResponseDecoder.class.getName());
@Override
protected Object decode(ChannelHandlerContext channelHandlerContext, Channel channel, Object msg) throws Exception {
long receiveTime = System.currentTimeMillis();
if (!(msg instanceof ChannelBuffer)) {
return msg;
}
//System.out.println("Response recvtime: " + System.currentTimeMillis());
List<Object> messages = null;
ChannelBuffer cb = (ChannelBuffer) NettyCodecUtils.getAttachment(channelHandlerContext, Constants.ATTACHMENT_BYTEBUFFER);
ChannelBuffer cb_ = (ChannelBuffer) msg;
if (cb == null) {
cb = cb_;
} else {
cb.writeBytes(cb_);
}
ChannelHandlerContext encodeCtx = channelHandlerContext.getPipeline().getContext("encoder");
Integer protoFlag = (Integer)NettyCodecUtils.getAttachment(encodeCtx, Constants.ATTACHMENT_CUSTOM_PROTO_FLAG);
if (protoFlag != null && protoFlag.intValue() == 1) {
RpcResponse rpcResponse = getCustomPackage(channelHandlerContext, channel, cb, encodeCtx);
if (rpcResponse == null) {
return null;
}
if (messages == null) {
messages = new ArrayList<Object>();
}
messages.add(rpcResponse);
return messages;
}
int lastReadIndex = cb.readerIndex();
while (cb.readable()) {
if (cb.readableBytes() < Constants.PKG_LEAST_LENGTH) {
setAttachment(channelHandlerContext, channel, cb, lastReadIndex);
break;
}
byte stx = cb.readByte();
if (stx != (byte)'(') {
log.error("Invalid packge stx.");
channelHandlerContext.getChannel().close();
throw new IllegalArgumentException("Request decode error: invalid package stx " + stx);
}
int headLength = cb.readInt();
int bodyLength = cb.readInt();
if (cb.readableBytes() < headLength + bodyLength + 1) {
setAttachment(channelHandlerContext, channel, cb, lastReadIndex);
break;
}
byte[] headBytes = new byte[headLength];
byte[] bodyBytes = new byte[bodyLength];
cb.readBytes(headBytes);
cb.readBytes(bodyBytes);
byte etx = cb.readByte();
if (etx != ')') {
log.error("Invalid package etx.");
channelHandlerContext.getChannel().close();
throw new IllegalArgumentException("Request decode error: invalid package etx " + etx);
}
RpcResponse rpcResponse = null;
//parse protobuf
try {
Head.CRpcHead pbHead = Head.CRpcHead.parseFrom(headBytes);
String serviceMethodName = pbHead.getMethodName().toStringUtf8();
//String[] splits = serviceMethodName.split(":");
//String serviceName = splits[0];
//String methodName = splits[1];
rpcResponse = new RpcResponse();
rpcResponse.setSeq(pbHead.getSequence());
rpcResponse.setErrno(pbHead.getErr());
if (pbHead.getErrMsg() != null && !pbHead.getErrMsg().isEmpty()) {
rpcResponse.setError(new Exception(pbHead.getErrMsg().toStringUtf8()));
} else {
rpcResponse.setError(null);
}
if (pbHead.getErr() == 0) {
rpcResponse.setResultObj(bodyBytes);
} else {
rpcResponse.setResultObj(null);
}
} catch (com.google.protobuf.InvalidProtocolBufferException ex) {
rpcResponse = null;
log.error("RPC Response parse failed.");
throw new IllegalArgumentException("Response decode error: protobuf parse failed.");
}
if (rpcResponse != null) {
if (messages == null) {
messages = new ArrayList<Object>();
}
messages.add(rpcResponse);
lastReadIndex = cb.readerIndex();
} else {
setAttachment(channelHandlerContext, channel, cb, lastReadIndex);
break;
}
}
return messages;
}
private RpcResponse getCustomPackage(ChannelHandlerContext channelHandlerContext, Channel channel, ChannelBuffer cb, ChannelHandlerContext encodeCtx) {
byte[] tmp_buff = new byte[cb.readableBytes()];
cb.readBytes(tmp_buff);
CustomPackageLengthChecker lengthChecker = (CustomPackageCodec) NettyCodecUtils.getAttachment(encodeCtx, Constants.ATTACHMENT_CUSTOM_PROTO_CODEC);
int checkRet = lengthChecker.checkPackageLength(tmp_buff);
if (checkRet < 0) {
return null;
} else if (checkRet == 0 || checkRet > tmp_buff.length) {
cb.resetReaderIndex();
setAttachment(channelHandlerContext, channel, cb, 0);
return null;
} else if (checkRet <= tmp_buff.length) {
if (checkRet < tmp_buff.length) {
tmp_buff = null;
tmp_buff = new byte[checkRet];
cb.resetReaderIndex();
cb.readBytes(tmp_buff);
}
long sequence = 0;
Long sequenceObj = (Long)NettyCodecUtils.getAttachment(encodeCtx, Constants.ATTACHMENT_CUSTOM_PROTO_SEQUENCE);
if (sequenceObj == null) {
CustomPackageCodec packageCodec = (CustomPackageCodec)lengthChecker;
sequence = packageCodec.decodeSequence(tmp_buff);
} else {
sequence = sequenceObj.longValue();
}
RpcResponse rpcResponse = new RpcResponse();
rpcResponse.setSeq(sequence);
rpcResponse.setResultObj(tmp_buff);
return rpcResponse;
}
return null;
}
private void setAttachment(ChannelHandlerContext ctx, Channel channel, ChannelBuffer cb, int lastReadIndex) {
cb.readerIndex(lastReadIndex);
if (!(cb instanceof DynamicChannelBuffer) || cb.writerIndex() > 102400) {
ChannelBuffer db = ChannelBuffers.dynamicBuffer(cb.readableBytes() * 2, channel.getConfig().getBufferFactory());
db.writeBytes(cb);
cb = db;
}
NettyCodecUtils.setAttachment(ctx, Constants.ATTACHMENT_BYTEBUFFER, cb);
}
}