diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java index 22572f08985..ce976a870d9 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseSubscribers.java @@ -282,7 +282,10 @@ public class ResponseSubscribers { @Override public void onNext(List items) { try { - out.write(items.toArray(Utils.EMPTY_BB_ARRAY)); + ByteBuffer[] buffers = items.toArray(Utils.EMPTY_BB_ARRAY); + do { + out.write(buffers); + } while (Utils.hasRemaining(buffers)); } catch (IOException ex) { close(); subscription.cancel(); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index 09457b795b4..511bd94ede3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -665,6 +665,14 @@ public final class Utils { return false; } + public static boolean hasRemaining(ByteBuffer[] bufs) { + for (ByteBuffer buf : bufs) { + if (buf.hasRemaining()) + return true; + } + return false; + } + public static long remaining(List bufs) { long remain = 0; for (ByteBuffer buf : bufs) { diff --git a/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java b/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java index c5f6d906983..7f361b8cf52 100644 --- a/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java +++ b/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8237470 + * @bug 8237470 8299015 * @summary Confirm HttpResponse.BodySubscribers#ofFile(Path) * works with default and non-default file systems * when SecurityManager is enabled @@ -66,11 +66,15 @@ import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodySubscriber; import java.net.http.HttpResponse.BodySubscribers; +import java.nio.Buffer; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Map; +import java.util.concurrent.Flow; +import java.util.stream.IntStream; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -199,6 +203,30 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { } } + // A large enough number of buffers to gather from, in an attempt to provoke a partial + // write. Loosely based on the value of _SC_IOV_MAX, to trigger partial gathering write. + private static final int NUM_GATHERING_BUFFERS = 1024 + 1; + + @Test + public void testSubscribersWritesAllBytes() throws Exception { + var buffers = IntStream.range(0, NUM_GATHERING_BUFFERS) + .mapToObj(i -> new byte[10]) + .map(ByteBuffer::wrap).toList(); + int expectedSize = buffers.stream().mapToInt(Buffer::remaining).sum(); + + var subscriber = BodySubscribers.ofFile(defaultFsPath); + subscriber.onSubscribe(new Flow.Subscription() { + @Override + public void request(long n) { } + @Override + public void cancel() { } + }); + subscriber.onNext(buffers); + subscriber.onComplete(); + buffers.forEach(b -> assertEquals(b.remaining(), 0) ); + assertEquals(expectedSize, Files.size(defaultFsPath)); + } + @BeforeTest public void setup() throws Exception { sslContext = new SimpleSSLContext().get();