/* * Copyright (c) 2018, 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 * @summary Ensure that the POST method is retied when the property is set. * @run testng/othervm -Djdk.httpclient.enableAllMethodRetry RetryPost * @run testng/othervm -Djdk.httpclient.enableAllMethodRetry=true RetryPost */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpResponse.BodyHandlers.ofString; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.testng.Assert.assertEquals; public class RetryPost { FixedLengthServer fixedLengthServer; String httpURIFixLen; static final String RESPONSE_BODY = "You use a glass mirror to see your face: you use works of art to see your soul."; @DataProvider(name = "uris") public Object[][] variants() { return new Object[][] { { httpURIFixLen, true }, { httpURIFixLen, false }, }; } static final int ITERATION_COUNT = 3; static final String REQUEST_BODY = "Body"; @Test(dataProvider = "uris") void testSynchronousPOST(String url, boolean sameClient) throws Exception { out.print("---\n"); HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = HttpClient.newBuilder().proxy(NO_PROXY).build(); HttpRequest request = HttpRequest.newBuilder(URI.create(url)) .POST(BodyPublishers.ofString(REQUEST_BODY)) .build(); HttpResponse response = client.send(request, ofString()); String body = response.body(); out.println(response + ": " + body); assertEquals(response.statusCode(), 200); assertEquals(body, RESPONSE_BODY); } } @Test(dataProvider = "uris") void testAsynchronousPOST(String url, boolean sameClient) { out.print("---\n"); HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = HttpClient.newBuilder().proxy(NO_PROXY).build(); HttpRequest request = HttpRequest.newBuilder(URI.create(url)) .POST(BodyPublishers.ofString(REQUEST_BODY)) .build(); client.sendAsync(request, ofString()) .thenApply(r -> { out.println(r + ": " + r.body()); return r; }) .thenApply(r -> { assertEquals(r.statusCode(), 200); return r; }) .thenApply(HttpResponse::body) .thenAccept(b -> assertEquals(b, RESPONSE_BODY)) .join(); } } /** * A server that issues a valid fixed-length reply on even requests, and * immediately closes the connection on odd requests ( tick-tock ). */ static class FixedLengthServer extends Thread implements AutoCloseable { static final String RESPONSE_HEADERS = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Content-Length: " + RESPONSE_BODY.length() + "\r\n" + "Connection: close\r\n\r\n"; static final String RESPONSE = RESPONSE_HEADERS + RESPONSE_BODY; private final ServerSocket ss; private volatile boolean closed; private int invocationTimes; FixedLengthServer() throws IOException { super("FixedLengthServer"); ss = new ServerSocket(); ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); this.start(); } public int getPort() { return ss.getLocalPort(); } @Override public void run() { while (!closed) { try (Socket s = ss.accept()) { invocationTimes++; out.print("FixedLengthServer: got connection "); if ((invocationTimes & 0x1) == 0x1) { out.println(" closing immediately"); s.close(); continue; } InputStream is = s.getInputStream(); URI requestMethod = readRequestMethod(is); out.print(requestMethod + " "); URI uriPath = readRequestPath(is); out.println(uriPath); readRequestHeaders(is); byte[] body = is.readNBytes(4); assert body.length == REQUEST_BODY.length() : "Unexpected request body " + body.length; OutputStream os = s.getOutputStream(); out.println("Server: writing response bytes"); byte[] responseBytes = RESPONSE.getBytes(US_ASCII); os.write(responseBytes); } catch (IOException e) { if (!closed) throw new UncheckedIOException("Unexpected", e); } } } @Override public void close() { if (closed) return; closed = true; try { ss.close(); } catch (IOException e) { throw new UncheckedIOException("Unexpected", e); } } static final byte[] requestEnd = new byte[] { '\r', '\n', '\r', '\n' }; // Read the request method static URI readRequestMethod(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(); int r; while ((r = is.read()) != -1 && r != 0x20) { sb.append((char)r); } return URI.create(sb.toString()); } // Read the request URI path static URI readRequestPath(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(); int r; while ((r = is.read()) != -1 && r != 0x20) { sb.append((char)r); } return URI.create(sb.toString()); } // Read until the end of a HTTP request headers static void readRequestHeaders(InputStream is) throws IOException { int requestEndCount = 0, r; while ((r = is.read()) != -1) { if (r == requestEnd[requestEndCount]) { requestEndCount++; if (requestEndCount == 4) { break; } } else { requestEndCount = 0; } } } } static String serverAuthority(FixedLengthServer server) { return InetAddress.getLoopbackAddress().getHostName() + ":" + server.getPort(); } @BeforeTest public void setup() throws Exception { fixedLengthServer = new FixedLengthServer(); httpURIFixLen = "http://" + serverAuthority(fixedLengthServer) + "/http1/fixed/baz"; } @AfterTest public void teardown() throws Exception { fixedLengthServer.close(); } }