docs: public synchronized R receive() throws IOException {
          if (readChannelDone && receiver == null) {
              //we have received the last frame, we just shut down and return
              //it would probably make more sense to have the last channel responsible for this
              //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;
                  }
                  //if we read data into a frame we just return immediately, even if there is more remaining
                  //see https://issues.jboss.org/browse/UNDERTOW-410
                  //basically if we don't do this we loose some message ordering semantics
                  //as the second message may be processed before the first one
  
                  //this is problematic for HTTPS, where the read listener may also be invoked by a queued task
                  //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 (moreData) {
                              receiver = newChannel;
                          }
  
                          if(isLastFrameReceived()) {
                              handleLastFrame(newChannel);
                          }
                      } else {
                          frameData.close();
                      }
                      return newChannel;
                  }
              }
              return null;
          } catch (IOException|RuntimeException|Error 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 {
              //if the receive caused the channel to break the close listener may be have been called
              //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();
  
                  }
              }
          }
      }