/**
   * Used to add additional HTTP message chunk content to {@link #inputContentBuffer}.
   *
   * @param requestedLen how much content should attempt to be read, <code>-1</code> means read till
   *     the end of the message.
   * @return the number of bytes actually read
   * @throws IOException if an I/O error occurs while reading content
   */
  private int fill(final int requestedLen) throws IOException {

    int read = 0;
    while ((requestedLen == -1 || read < requestedLen) && httpHeader.isExpectContent()) {

      final HttpContent c = blockingRead();

      final boolean isLast = c.isLast();
      // Check if HttpContent is chunked message trailer w/ headers
      checkHttpTrailer(c);

      final Buffer b;
      try {
        b = c.getContent();
      } catch (HttpBrokenContentException e) {
        final Throwable cause = e.getCause();
        throw Exceptions.makeIOException(cause != null ? cause : e);
      }

      read += b.remaining();
      updateInputContentBuffer(b);
      c.recycle();

      if (isLast) {
        finished();
        break;
      }
    }

    if (read > 0 || requestedLen == 0) {
      return read;
    }

    return -1;
  }
  /** @see java.io.Reader#ready() */
  public boolean ready() {

    if (closed) {
      return false;
    }
    if (!processingChars) {
      throw new IllegalStateException();
    }
    return (inputContentBuffer.hasRemaining() || httpHeader.isExpectContent());
  }
  /**
   * Used to convert bytes to chars.
   *
   * @param requestedLen how much content should attempt to be read
   * @return the number of chars actually read
   * @throws IOException if an I/O error occurs while reading content
   */
  private int fillChars(final int requestedLen, final CharBuffer dst) throws IOException {

    int read = 0;

    // 1) Check pre-decoded singleCharBuf
    if (dst != singleCharBuf && singleCharBuf.hasRemaining()) {
      dst.put(singleCharBuf.get());
      read = 1;
    }

    // 2) Decode available byte[] -> char[]
    if (inputContentBuffer.hasRemaining()) {
      read += fillAvailableChars(requestedLen - read, dst);
    }

    if (read >= requestedLen) {
      dst.flip();
      return read;
    }

    // 3) If we don't expect more data - return what we've read so far
    if (!httpHeader.isExpectContent()) {
      dst.flip();
      return read > 0 ? read : -1;
    }

    // 4) Try to read more data (we may block)
    CharsetDecoder decoderLocal = getDecoder();

    boolean isNeedMoreInput =
        false; // true, if content in composite buffer is not enough to produce even 1 char
    boolean last = false;

    while (read < requestedLen && httpHeader.isExpectContent()) {

      if (isNeedMoreInput || !inputContentBuffer.hasRemaining()) {
        final HttpContent c = blockingRead();
        updateInputContentBuffer(c.getContent());
        last = c.isLast();

        c.recycle();
        isNeedMoreInput = false;
      }

      final ByteBuffer bytes = inputContentBuffer.toByteBuffer();

      final int bytesPos = bytes.position();
      final int dstPos = dst.position();

      final CoderResult result = decoderLocal.decode(bytes, dst, false);

      final int producedChars = dst.position() - dstPos;
      final int consumedBytes = bytes.position() - bytesPos;

      read += producedChars;

      if (consumedBytes > 0) {
        bytes.position(bytesPos);
        inputContentBuffer.position(inputContentBuffer.position() + consumedBytes);
        if (readAheadLimit == -1) {
          inputContentBuffer.shrink();
        }
      } else {
        isNeedMoreInput = true;
      }

      if (last || result == CoderResult.OVERFLOW) {
        break;
      }
    }

    dst.flip();

    if (last && read == 0) {
      read = -1;
    }
    return read;
  }