Code review comment: Now that we've repeat() and `length` I guess we can simplify this a bit and avoid extra allocation of `Pair` type & avoid extra filter , onComplete and flatmap. We could also move validations to within this map. I didn't test below code but this is something in my mind.  ```java public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long length, int blockSize) {     final long[] currentTotalLength = new long[1];     return Flux.just(true)         .repeat()         .map(ignore -> {              byte[] buffer = new byte[blockSize];              int totalBytesRead = 0;              while (totalBytesRead < blockSize) {                  int numBytesRead = data.read(buffer, totalBytesRead, blockSize - totalBytesRead);                  if (numBytesRead == -1)  {                      break;                  } else {                     totalBytesRead += numBytesRead;                   }              }              if (totalBytesRead == 0) {                  // If totalBytesRead is zero after the above loop then means either we reached EOF                  // or blockSize is zero                  if (currentTotalLength[0] < length) {                      throw "Stream has less than provided length!"                  } else {                     // Emit an empty ByteBuffer that get filtered out by below takeUntil.                     return ByteBuffer.wrap(buffer, 0, 0);                  }              } else {                  currentTotalLength[0] += totalBytesRead;                  if (currentTotalLength[0] > length) {                      throw "Stream has more than provided length!"                  } else {                     return ByteBuffer.wrap(buffer, 0, totalBytesRead);                  }              }          })          .takeUntil(bb -> !bb.hasRemaining()); ```

Code under review:
public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long length, int blockSize) {
        Pair pair = new Pair();
        final long[] currentTotalLength = new long[1];
        return Flux.just(true)
            .repeat()
            .map(ignore -> {
                byte[] buffer = new byte[blockSize];
                try {
                    int numBytes = data.read(buffer);
                    if (numBytes > 0) {
                        currentTotalLength[0] += numBytes;
                        return pair.buffer(ByteBuffer.wrap(buffer, 0, numBytes)).readBytes(numBytes);
                    } else {
                        return pair.buffer(null).readBytes(numBytes);
                    }
                } catch (IOException ioe) {
                    throw Exceptions.propagate(ioe);
                }
            })
            .takeUntil(p -> p.readBytes() == -1)
            .filter(p -> p.readBytes() > 0)
            .flatMap(p -> {
                if (currentTotalLength[0] < length) {
                    throw LOGGER.logExceptionAsError(new UnexpectedLengthException(
                        String.format("Request body emitted %d bytes, less than the expected %d bytes.",
                            currentTotalLength[0], length), currentTotalLength[0], length));
                } else if (currentTotalLength[0] > length) {
                    throw LOGGER.logExceptionAsError(new UnexpectedLengthException(
                        String.format("Request body emitted %d bytes, more than the expected %d bytes.",
                            currentTotalLength[0], length), currentTotalLength[0], length));
                } else {
                    return Flux.just(p.buffer());
                }
            });
    }

Line commented on: int numBytes = data.read(buffer);

Revised code:
public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long length, int blockSize) {
        Pair pair = new Pair();
        final long[] currentTotalLength = new long[1];
        return Flux.just(true)
            .repeat()
            .map(ignore -> {
                byte[] buffer = new byte[blockSize];
                try {
                    int numBytes = data.read(buffer);
                    if (numBytes > 0) {
                        currentTotalLength[0] += numBytes;
                        return pair.buffer(ByteBuffer.wrap(buffer, 0, numBytes)).readBytes(numBytes);
                    } else {
                        return pair.buffer(null).readBytes(numBytes);
                    }
                } catch (IOException ioe) {
                    throw Exceptions.propagate(ioe);
                }
            })
            .takeUntil(p -> p.readBytes() == -1 || currentTotalLength[0] > length)
            .filter(p -> p.readBytes() > 0)
            .flatMap(p -> {
                if (currentTotalLength[0] < length) {
                    throw LOGGER.logExceptionAsError(new UnexpectedLengthException(
                        String.format("Request body emitted %d bytes, less than the expected %d bytes.",
                            currentTotalLength[0], length), currentTotalLength[0], length));
                } else if (currentTotalLength[0] > length) {
                    throw LOGGER.logExceptionAsError(new UnexpectedLengthException(
                        String.format("Request body emitted %d bytes, more than the expected %d bytes.",
                            currentTotalLength[0], length), currentTotalLength[0], length));
                } else {
                    return Flux.just(p.buffer());
                }
            });
    }