Search in sources :

Example 1 with ReferenceCountedPooled

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();
            }
        }
    }
}
Also used : ClosedChannelException(java.nio.channels.ClosedChannelException) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) ReferenceCountedPooled(io.undertow.util.ReferenceCountedPooled) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer)

Aggregations

PooledByteBuffer (io.undertow.connector.PooledByteBuffer)1 ReferenceCountedPooled (io.undertow.util.ReferenceCountedPooled)1 IOException (java.io.IOException)1 ByteBuffer (java.nio.ByteBuffer)1 ClosedChannelException (java.nio.channels.ClosedChannelException)1