use of io.undertow.util.ReferenceCountedPooled in project undertow by undertow-io.
the class AbstractFramedChannel method receive.
/**
* receive method, returns null if no frame is ready. Otherwise returns a
* channel that can be used to read the frame contents.
* <p>
* Calling this method can also have the side effect of making additional data available to
* existing source channels. In general if you suspend receives or don't have some other way
* of calling this method then it can prevent frame channels for being fully consumed.
*/
public synchronized R receive() throws IOException {
if (readChannelDone && receiver == null) {
//however it is much simpler just to have it here
if (readData != null) {
readData.close();
readData = null;
}
channel.getSourceChannel().suspendReads();
channel.getSourceChannel().shutdownReads();
return null;
}
ReferenceCountedPooled pooled = this.readData;
boolean hasData;
if (pooled == null) {
pooled = allocateReferenceCountedBuffer();
if (pooled == null) {
return null;
}
hasData = false;
} else if (pooled.isFreed()) {
//we attempt to re-used an existing buffer
if (!pooled.tryUnfree()) {
pooled = allocateReferenceCountedBuffer();
if (pooled == null) {
return null;
}
} else {
pooled.getBuffer().limit(pooled.getBuffer().capacity());
}
hasData = false;
} else {
hasData = pooled.getBuffer().hasRemaining();
}
boolean forceFree = false;
int read = 0;
try {
if (!hasData) {
pooled.getBuffer().clear();
read = channel.getSourceChannel().read(pooled.getBuffer());
if (read == 0) {
//no data, we just free the buffer
forceFree = true;
return null;
} else if (read == -1) {
forceFree = true;
readChannelDone = true;
lastDataRead();
return null;
} else if (isLastFrameReceived() && frameDataRemaining == 0) {
//we got data, although we should have received the last frame
forceFree = true;
markReadsBroken(new ClosedChannelException());
}
pooled.getBuffer().flip();
}
if (frameDataRemaining > 0) {
if (frameDataRemaining >= pooled.getBuffer().remaining()) {
frameDataRemaining -= pooled.getBuffer().remaining();
if (receiver != null) {
//we still create a pooled view, this means that if the buffer is still active we can re-used it
//which prevents attacks based on sending lots of small fragments
ByteBuffer buf = pooled.getBuffer().duplicate();
pooled.getBuffer().position(pooled.getBuffer().limit());
PooledByteBuffer frameData = pooled.createView(buf);
receiver.dataReady(null, frameData);
} else {
//we are dropping a frame
pooled.close();
readData = null;
}
if (frameDataRemaining == 0) {
receiver = null;
}
return null;
} else {
ByteBuffer buf = pooled.getBuffer().duplicate();
buf.limit((int) (buf.position() + frameDataRemaining));
pooled.getBuffer().position((int) (pooled.getBuffer().position() + frameDataRemaining));
frameDataRemaining = 0;
PooledByteBuffer frameData = pooled.createView(buf);
if (receiver != null) {
receiver.dataReady(null, frameData);
} else {
//we are dropping the frame
frameData.close();
}
receiver = null;
}
//and not by the selector mechanism
return null;
}
FrameHeaderData data = parseFrame(pooled.getBuffer());
if (data != null) {
PooledByteBuffer frameData;
if (data.getFrameLength() >= pooled.getBuffer().remaining()) {
frameDataRemaining = data.getFrameLength() - pooled.getBuffer().remaining();
frameData = pooled.createView(pooled.getBuffer().duplicate());
pooled.getBuffer().position(pooled.getBuffer().limit());
} else {
ByteBuffer buf = pooled.getBuffer().duplicate();
buf.limit((int) (buf.position() + data.getFrameLength()));
pooled.getBuffer().position((int) (pooled.getBuffer().position() + data.getFrameLength()));
frameData = pooled.createView(buf);
}
AbstractFramedStreamSourceChannel<?, ?, ?> existing = data.getExistingChannel();
if (existing != null) {
if (data.getFrameLength() > frameData.getBuffer().remaining()) {
receiver = (R) existing;
}
existing.dataReady(data, frameData);
if (isLastFrameReceived()) {
handleLastFrame(existing);
}
return null;
} else {
boolean moreData = data.getFrameLength() > frameData.getBuffer().remaining();
R newChannel = createChannel(data, frameData);
if (newChannel != null) {
if (!newChannel.isComplete()) {
receivers.add(newChannel);
}
if (moreData) {
receiver = newChannel;
}
if (isLastFrameReceived()) {
handleLastFrame(newChannel);
}
} else {
frameData.close();
}
return newChannel;
}
}
return null;
} catch (IOException | RuntimeException e) {
//something has code wrong with parsing, close the read side
//we don't close the write side, as the underlying implementation will most likely want to send an error
markReadsBroken(e);
forceFree = true;
throw e;
} finally {
//which will make readData null
if (readData != null) {
if (!pooled.getBuffer().hasRemaining() || forceFree) {
if (pooled.getBuffer().limit() * 2 > pooled.getBuffer().capacity() || forceFree) {
//if we have used more than half the buffer we don't allow it to be re-aquired
readData = null;
}
//even though this is freed we may un-free it if we get a new packet
//this prevents many small reads resulting in a large number of allocated buffers
pooled.close();
}
}
}
}
Aggregations