HttpResponse.BodyHandlers.ofFile may lose data on response with small chunk fragments

Chris Hegarty chegar999 at gmail.com
Mon Dec 19 14:14:28 UTC 2022


Thanks Dawid,

I've added 11 to the affects version, and raised the following PR:

https://github.com/openjdk/jdk/pull/11722

-Chris.

On 19/12/2022 11:38, Dawid Weiss wrote:
> 
> Thanks Chris.
> 
> Not sure whether this matters but the same bug/code goes back all the 
> way to JDK 11... (the issue lists JDK 17+ as affected):
> 
> https://github.com/openjdk/jdk11u/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java#L207-L215 
> <https://github.com/openjdk/jdk11u/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java#L207-L215>
> 
> Dawid
> 
> On Mon, Dec 19, 2022 at 10:23 AM Chris Hegarty <chegar999 at gmail.com 
> <mailto:chegar999 at gmail.com>> wrote:
> 
>     Hi Dawid,
> 
>     Thanks for raising this issue. I filed the following JIRA to track it:
>     https://bugs.openjdk.org/browse/JDK-8299015
>     <https://bugs.openjdk.org/browse/JDK-8299015>
> 
>     -Chris.
> 
>     On 17/12/2022 18:45, Dawid Weiss wrote:
>      > Hello,
>      >
>      > I've discovered a bug in the implementation of PathSubscriber in how
>      > byte buffers are flushed/ written to the file channel. Specifically,
>      > this method in PathSubscriber [2]:
>      >
>      > @Override
>      > public void onNext(List<ByteBuffer> items) {
>      >      try {
>      >          out.write(items.toArray(Utils.EMPTY_BB_ARRAY));
>      > ...
>      >
>      > the implementation assumes out.write(ByteBuffer[]) always writes the
>      > content of all buffers - this isn't the case [1] and the method can
>      > write any number of bytes from input buffers.
>      >
>      > The manifestation of this bug is that HttpClient can fail to
>     write all
>      > the data in a heavily chunked http response (!).
>      >
>      > The longer story is that I discovered the above while attempting to
>      > debug data inconsistency errors in a HttpClient fetching chunked
>      > responses. I'm on Windows and the problem is fairly easy to
>     reproduce.
>      > Wireshark (packet interceptor) shows full data being transmitted,
>      > HttpClient fails to write all of it to disk with:
>      >
>      > httpClient.send(request,
>     HttpResponse.BodyHandlers.ofFile(outputFile));
>      >
>      > The bug is fairly evident - I'm not sure how it slipped
>     undetected for
>      > so long. The non-exhaustive FileChannel.write(ByteBuffer[]) behavior
>      > can be demonstrated by this code:
>      >
>      > var file = Files.createTempFile("tmp", "tmp");
>      > try (var out = FileChannel.open(file, StandardOpenOption.WRITE,
>      > StandardOpenOption.CREATE)) {
>      >    // Create a lot of smallish buffers.
>      >    ByteBuffer[] buffers = new ByteBuffer[100];
>      >    for (int i = 0; i < buffers.length; i++) {
>      >      buffers[i] = ByteBuffer.wrap(new byte[100]);
>      >    }
>      >
>      >    System.out.println(
>      >        "Data in buffers: " +
>      > Arrays.stream(buffers).mapToLong(Buffer::remaining).sum());
>      >    long len = out.write(buffers);
>      >    System.out.println("Written: " + len);
>      >    System.out.println(
>      >        "Data in buffers: " +
>      > Arrays.stream(buffers).mapToLong(Buffer::remaining).sum());
>      > } finally {
>      >    Files.delete(file);
>      > }
>      >
>      > This prints the following for me on Windows:
>      >
>      > Data in buffers: 10000
>      > Written: 1600
>      > Data in buffers: 8400
>      >
>      > Clearly, PathSubscriber should have a loop to exhaust all buffers.
>      > Seems like a trivial fix, so I don't include the patch (and I can't
>      > file a proper Jira issue because I have no access to Jira).
>      >
>      > Dawid
>      >
>      > [1]
>     https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/FileChannel.html#write(java.nio.ByteBuffer%5B%5D)
>     <https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/FileChannel.html#write(java.nio.ByteBuffer%5B%5D)>
>      >
>      > [2]
>     https://github.com/openjdk/jdk/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java#L285
>     <https://github.com/openjdk/jdk/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java#L285>
> 


More information about the net-dev mailing list