| /* |
| * 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<String> 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(); |
| } |
| } |