Source:
<code>public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long length, int blockSize) { final long[] currentTotalLength = new long[1]; return Flux.range(0, (int) Math.ceil((double) length / (double) blockSize)) .map(i -> i * blockSize) .concatMap(pos -> Mono.fromCallable(() -> { long count = pos + blockSize > length ? length - pos : blockSize; byte[] cache = new byte[(int) count]; int lastIndex = data.read(cache); <START> if (lastIndex == -1 && currentTotalLength[0] < length) { <END> throw LOGGER.logExceptionAsError(new UnexpectedLengthException( String.format("Request body emitted %d bytes, less than the expected %d bytes.", currentTotalLength[0], length), currentTotalLength[0], length)); } currentTotalLength[0] += lastIndex; return ByteBuffer.wrap(cache); })) .doOnComplete(() -> { try { if (data.available() > 0) { long totalLength = currentTotalLength[0] + data.available(); throw LOGGER.logExceptionAsError(new UnexpectedLengthException( String.format("Request body emitted %d bytes, more than the expected %d bytes.", totalLength, length), totalLength, length)); } } catch (IOException e) { throw LOGGER.logExceptionAsError(new RuntimeException("I/O errors occurs. Error details: " + e.getMessage())); } }); }</code><technical_language>This is closer implementation handle a stream is buffering slowly is issue this random chunks of data missing. current implementation a scenario I 5 100 byte chunks I this happen: Read 100 Read 100 Read 10 (slow buffering) Read 10 (slow buffering) Read 100 Throw UnexpectedLengthException, expected 500 320 more data loading stream. Instead a looping mechanism concatMap emissions if I read 10 I try read more of scenarios 1. I filled current buffer, wrap this trigger next concatMap emission 2. I hit end of file, wrap this trigger complete 3. I hit end of file I expected, trigger error scenario Read 100 Read 100, Read 10 (slow buffering), read 10 (slow buffering), read 80 Read 100 Read 100 is good here, send data service</technical_language>

Target:
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()); } }); }