/** * */ package io.nettythrift.core; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; /** * @author HouKx * */ public class TNettyTransport extends TTransport { private final Channel channel; private final ByteBuf in; private ByteBuf out; private final int initialReaderIndex; private final int initialBufferPosition; private int bufferPosition; private int bufferEnd; private final byte[] buffer; // public static final int MOD_R = 1; // public static final int MOD_W = 2; // public static final int MOD_RW = MOD_R | MOD_W; private boolean hasFlush; public TNettyTransport(Channel channel, ByteBuf in) { this.channel = channel; this.in = in; this.initialReaderIndex = in != null ? in.readerIndex() : 0; if (in != null) { if (!in.hasArray()) { buffer = null; bufferPosition = 0; initialBufferPosition = bufferEnd = -1; } else { buffer = in.array(); initialBufferPosition = bufferPosition = in.arrayOffset() + in.readerIndex(); bufferEnd = bufferPosition + in.readableBytes(); // Without this, reading from a !in.hasArray() buffer will // advance the readerIndex // of the buffer, while reading from a in.hasArray() buffer will // not advance the // readerIndex, and this has led to subtle bugs. This should // help to identify // those problems by making things more consistent. in.readerIndex(in.readerIndex() + in.readableBytes()); } } else { buffer = null; initialBufferPosition = bufferPosition = bufferEnd = 0; } } @Override public boolean isOpen() { return channel.isOpen(); } @Override public void open() throws TTransportException { // no-op } @Override public void close() { // no-op channel.close(); } @Override public int read(byte[] bytes, int offset, int length) throws TTransportException { if (getBytesRemainingInBuffer() >= 0) { int _read = Math.min(getBytesRemainingInBuffer(), length); System.arraycopy(getBuffer(), getBufferPosition(), bytes, offset, _read); consumeBuffer(_read); // System.out.printf(getClass().getSimpleName() + // "::read[Remaining]: offset=%d, length=%d, bytes=%s\n", // offset, _read, Arrays.toString(bytes)); return _read; } else { int _read = Math.min(in.readableBytes(), length); // System.out.printf("curReaderIndex=%d, readable? %s, size = %d, // _read = %d, length = // %d\n",in.readerIndex(),in.isReadable(),in.readableBytes(), // _read,length); in.readBytes(bytes, offset, _read); // System.out.printf(getClass().getSimpleName() + "::read: // offset=%d, length=%d, bytes=%s\n", offset, _read, // Arrays.toString(bytes)); return _read; } } @Override public int readAll(byte[] bytes, int offset, int length) throws TTransportException { if (read(bytes, offset, length) < length) { throw new TTransportException("Buffer doesn't have enough bytes to read"); } return length; } @Override public void write(byte[] bytes, int offset, int length) throws TTransportException { out.writeBytes(bytes, offset, length); } public ByteBuf getOutputBuffer() { return out; } public void setOutputBuffer(ByteBuf buf) { out = buf; } @Override public void flush() throws TTransportException { // Flush is a no-op: NiftyDispatcher will write the response to the // Channel, in order to // guarantee ordering of responses when required. hasFlush = true; } @Override public void consumeBuffer(int len) { bufferPosition += len; } @Override public byte[] getBuffer() { return buffer; } @Override public int getBufferPosition() { return bufferPosition; } @Override public int getBytesRemainingInBuffer() { return bufferEnd - bufferPosition; } public int getReadByteCount() { if (getBytesRemainingInBuffer() >= 0) { return getBufferPosition() - initialBufferPosition; } else { return in.readerIndex() - initialReaderIndex; } } public int getWrittenByteCount() { return out.writerIndex(); } public boolean isHasFlush() { return hasFlush; } }