/* * Copyright (C) 2012-2016 Facebook, 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 com.facebook.nifty.core; import org.apache.thrift.TApplicationException; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; /** * Wraps incoming channel buffer into TTransport and provides a output buffer. */ public class TNiftyTransport extends TTransport { private final Channel channel; private final ChannelBuffer in; private final ThriftTransportType thriftTransportType; private ChannelBuffer out; private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 1024; private final int initialReaderIndex; private final int initialBufferPosition; private int bufferPosition; private int bufferEnd; private final byte[] buffer; private TApplicationException tApplicationException; public TNiftyTransport(Channel channel, ChannelBuffer in, ThriftTransportType thriftTransportType) { this.channel = channel; this.in = in; this.thriftTransportType = thriftTransportType; this.out = ChannelBuffers.dynamicBuffer(DEFAULT_OUTPUT_BUFFER_SIZE); this.initialReaderIndex = in.readerIndex(); 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()); } } public TNiftyTransport(Channel channel, ThriftMessage message) { this(channel, message.getBuffer(), message.getTransportType()); } @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); return _read; } else { int _read = Math.min(in.readableBytes(), length); in.readBytes(bytes, offset, _read); 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 ChannelBuffer getOutputBuffer() { return out; } public void setOutputBuffer(ChannelBuffer buf) { out = buf; } public ThriftTransportType getTransportType() { return thriftTransportType; } @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. } @Override public void consumeBuffer(int len) { bufferPosition += len; } @Override @edu.umd.cs.findbugs.annotations.SuppressWarnings("EI_EXPOSE_REP") 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 getOutputBuffer().writerIndex(); } public void setTApplicationException(TApplicationException e) { tApplicationException = e; } public TApplicationException getTApplicationException() { return tApplicationException; } }