ITADN

[BUG] InputStreamContent closes caller InputStream between retry attempts, causing resettableContent() to fail with "Stream closed"

#49648Openmihhailmeshkov 创建于 2026-06-26
questioncustomer-reportedneeds-triage
M
mihhailmeshkovcommented
### Describe the bug `InputStreamContent` built from a mark/reset-capable `InputStream` loses replayability after the first failed HTTP attempt, causing all retries to fail with "stream closed" exception. The bug is in `RestProxyUtils`: when preparing the HTTP request body, it extracts the raw stream from the caller's `InputStreamContent`, wraps it in a `LengthValidatingInputStream`, and builds a new `InputStreamContent` from that wrapper (lines 65–67 and 142–147). The new `InputStreamContent` correctly detects the stream as replayable (because `markSupported()` returns true via delegation), stores `() -> resettableContent(LengthValidatingInputStream)` as its replay lambda, and calls `mark()` on the `LengthValidatingInputStream`. After each failed attempt the Reactor pipeline (via `MonoUsing`) closes the stream it obtained from `toStream()`. `LengthValidatingInputStream.close()` delegates unconditionally to `inner.close()`, which closes the caller's original stream. On the next retry, `resettableContent()` calls `LengthValidatingInputStream.reset()` → `inner.reset()` → the original stream throws `IOException: Stream closed`. Verified present in `azure-core` 1.55.4 and 1.58.0. --- ### Exception or Stack Trace ``` java.io.UncheckedIOException: java.io.IOException: Stream closed at com.azure.core.implementation.util.InputStreamContent.resettableContent(InputStreamContent.java:198) at com.azure.core.implementation.util.InputStreamContent.lambda$new$0(InputStreamContent.java:65) at com.azure.core.implementation.util.InputStreamContent.toStream(InputStreamContent.java:106) at reactor.core.publisher.MonoUsing.subscribe(MonoUsing.java:75) ... at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62) Caused by: java.io.IOException: Stream closed at MyStream.reset(NobeBufInputStream.java:85) at com.azure.core.implementation.http.rest.LengthValidatingInputStream.reset(LengthValidatingInputStream.java:95) at com.azure.core.implementation.util.InputStreamContent.resettableContent(InputStreamContent.java:195) ... Suppressed: java.util.concurrent.TimeoutException: Channel response timed out after 10000 milliseconds. ``` ### Expected behavior When `BinaryData.fromStream(is, length)` is called with a mark/reset-capable `InputStream` and retries are enabled, each retry attempt should be able to reset and re-read the stream. `LengthValidatingInputStream` should not close the underlying stream (or `RestProxyUtils` should shield the caller's stream from closure between retry attempts), since the stream is needed for subsequent retries. A minimal fix would be for `RestProxyUtils` to wrap the caller's stream in a non-closing decorator before handing it to `LengthValidatingInputStream`. Alternatively, `LengthValidatingInputStream` could avoid closing the inner stream when it is used in a retry context. Workaround: wrap the stream in a non-closing decorator before passing to `BinaryData.fromStream()`. ### Reproducer Test demonstrating both the bug and workaround is in attached file. AI-created, human-checked. [AzureSdkInputStreamRetryBugTest.java](https://github.com/user-attachments/files/29382005/AzureSdkInputStreamRetryBugTest.java)
0 条评论