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 6a58fe18fc9..fe10ef58d09 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 @@ -538,8 +538,8 @@ public class ResponseSubscribers { if (available != 0) return available; Iterator iterator = currentListItr; if (iterator != null && iterator.hasNext()) return 1; - if (buffers.isEmpty()) return 0; - return 1; + if (!buffers.isEmpty() && buffers.peek() != LAST_LIST ) return 1; + return available; } @Override diff --git a/test/jdk/java/net/httpclient/HttpInputStreamAvailableTest.java b/test/jdk/java/net/httpclient/HttpInputStreamAvailableTest.java new file mode 100644 index 00000000000..5cca88716b6 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpInputStreamAvailableTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2023, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8306040 + * @summary HttpResponseInputStream.available() returns 1 on empty stream + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @run junit/othervm HttpInputStreamAvailableTest + * + */ +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.net.URIBuilder; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class HttpInputStreamAvailableTest { + + private HttpServer server; + private int port; + static final String TEST_MESSAGE = "This is test message"; + static final int ZERO = 0; + + @BeforeAll + void setup() throws Exception { + InetAddress loopback = InetAddress.getLoopbackAddress(); + InetSocketAddress addr = new InetSocketAddress(loopback, 0); + server = HttpServer.create(addr, 0); + port = server.getAddress().getPort(); + FirstHandler fHandler = new FirstHandler(); + server.createContext("/NonZeroResponse/", fHandler); + SecondHandler sHandler = new SecondHandler(); + server.createContext("/ZeroResponse/", sHandler); + server.start(); + } + + @AfterAll + void teardown() throws Exception { + server.stop(0); + } + + @Test + public void test() throws Exception { + HttpClient client = HttpClient + .newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .build(); + + URI uri = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(port) + .path("/NonZeroResponse/") + .build(); + + HttpRequest request = HttpRequest + .newBuilder(uri) + .GET() + .build(); + + // Send a httpRequest and assert the bytes available + HttpResponse response = client.send(request, + HttpResponse.BodyHandlers.ofInputStream()); + try ( InputStream in = response.body()) { + in.readNBytes(2); + // this is not guaranteed, but a failure here would be surprising + assertEquals(TEST_MESSAGE.length() - 2, in.available()); + //read the remaining data + in.readAllBytes(); + //available should return 0 + assertEquals(ZERO, in.available()); + } + } + + @Test + public void test1() throws Exception { + HttpClient client = HttpClient + .newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .build(); + + URI uri = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(port) + .path("/ZeroResponse/") + .build(); + + HttpRequest request = HttpRequest + .newBuilder(uri) + .GET() + .build(); + + // Send a httpRequest and assert the bytes available + HttpResponse response = client.send(request, + HttpResponse.BodyHandlers.ofInputStream()); + try ( InputStream in = response.body()) { + assertEquals(ZERO, in.available()); + in.readAllBytes(); + assertEquals(ZERO, in.available()); + } + } + + static class FirstHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + try ( OutputStream os = exchange.getResponseBody()) { + byte[] workingResponse = TEST_MESSAGE.getBytes(); + exchange.sendResponseHeaders(200, workingResponse.length); + os.write(workingResponse); + os.flush(); + } + } + } + + static class SecondHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + exchange.sendResponseHeaders(204, -1); + } + } +}