/* * 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 8293786 * @summary Checks to see if the HttpClient can process a request to cancel a transmission from a remote if the server * does not process any data. The client should read all data from the server and close the connection. * @library /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer * @run testng/othervm/timeout=50 -Djdk.httpclient.HttpClient.log=all * PostPutTest */ import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2TestServer; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.IOException; import java.io.PrintStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; public class PostPutTest { Http2TestServer http2TestServer; URI warmupURI, testHandlerBasicURI, testHandlerCloseBosURI, testHandleNegativeContentLengthURI; static PrintStream testLog = System.err; // As per jdk.internal.net.http.WindowController.DEFAULT_INITIAL_WINDOW_SIZE final int DEFAULT_INITIAL_WINDOW_SIZE = (64 * 1024) - 1; // Add on a small amount of arbitrary bytes to see if client hangs when receiving RST_STREAM byte[] data = new byte[DEFAULT_INITIAL_WINDOW_SIZE + 10]; @BeforeTest public void setup() throws Exception { http2TestServer = new Http2TestServer(false, 0); http2TestServer.addHandler(new WarmupHandler(), "/Warmup"); http2TestServer.addHandler(new TestHandlerBasic(), "/TestHandlerBasic"); http2TestServer.addHandler(new TestHandlerCloseBos(), "/TestHandlerCloseBos"); http2TestServer.addHandler(new TestHandleNegativeContentLength(), "/TestHandleNegativeContentLength"); http2TestServer.start(); testLog.println("PostPutTest.setup(): Starting server"); warmupURI = new URI("http://" + http2TestServer.serverAuthority() + "/Warmup"); testHandlerBasicURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandlerBasic"); testHandlerCloseBosURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandlerCloseBos"); testHandleNegativeContentLengthURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandleNegativeContentLength"); testLog.println("PostPutTest.setup(): warmupURI: " + warmupURI); testLog.println("PostPutTest.setup(): testHandlerBasicURI: " + testHandlerBasicURI); testLog.println("PostPutTest.setup(): testHandlerCloseBosURI: " + testHandlerCloseBosURI); testLog.println("PostPutTest.setup(): testHandleNegativeContentLengthURI: " + testHandleNegativeContentLengthURI); } @AfterTest public void teardown() { testLog.println("PostPutTest.teardown(): Stopping server"); http2TestServer.stop(); data = null; } @DataProvider(name = "variants") public Object[][] variants() { HttpRequest over64kPost, over64kPut, over64kPostCloseBos, over64kPutCloseBos, over64kPostNegativeContentLength, over64kPutNegativeContentLength; over64kPost = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandlerBasicURI).build(); over64kPut = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandlerBasicURI).build(); over64kPostCloseBos = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandlerCloseBosURI).build(); over64kPutCloseBos = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandlerCloseBosURI).build(); over64kPostNegativeContentLength = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandleNegativeContentLengthURI).build(); over64kPutNegativeContentLength = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandleNegativeContentLengthURI).build(); return new Object[][] { { over64kPost, "POST data over 64k bytes" }, { over64kPut, "PUT data over 64k bytes" }, { over64kPostCloseBos, "POST data over 64k bytes with close bos" }, { over64kPutCloseBos, "PUT data over 64k bytes with close bos" }, { over64kPostNegativeContentLength, "POST data over 64k bytes with negative content length" }, { over64kPutNegativeContentLength, "PUT data over 64k bytes with negative content length" } }; } public HttpRequest getWarmupReq() { return HttpRequest.newBuilder() .GET() .uri(warmupURI) .build(); } @Test(dataProvider = "variants") public void testOver64kPUT(HttpRequest req, String testMessage) { testLog.println("PostPutTest: Performing test: " + testMessage); HttpClient hc = HttpClient.newBuilder().version(HTTP_2).build(); hc.sendAsync(getWarmupReq(), HttpResponse.BodyHandlers.ofString()).join(); hc.sendAsync(req, HttpResponse.BodyHandlers.ofString()).join(); /* If this test fails in timeout, it is likely due to one of two reasons: - The responseSubscriber is null, so no incoming frames are being processed by the client (See Stream::schedule) - The test server is for some reason not sending a RST_STREAM with the NO_ERROR flag set after sending an empty DATA frame with the END_STREAM flag set. */ } private static class TestHandlerBasic implements Http2Handler { @Override public void handle(Http2TestExchange exchange) throws IOException { // The input stream is not read in this bug as this will trigger window updates for the server. This bug // concerns the case where no updates are sent and the server instead tells the client to abort the transmission. exchange.sendResponseHeaders(200, 0); } } private static class TestHandlerCloseBos implements Http2Handler { @Override public void handle(Http2TestExchange exchange) throws IOException { // This case does actually cause the test to hang due to the body input stream being closed before it can send // the RST_STREAM frame. exchange.sendResponseHeaders(200, 0); exchange.getResponseBody().close(); } } private static class TestHandleNegativeContentLength implements Http2Handler { @Override public void handle(Http2TestExchange exchange) throws IOException { exchange.sendResponseHeaders(200, -1); } } private static class WarmupHandler implements Http2Handler { @Override public void handle(Http2TestExchange exchange) throws IOException { exchange.sendResponseHeaders(200, 0); } } }