| /* | |
| * Copyright (c) 2015, 2024, 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. | |
| */ | |
| import java.io.*; | |
| import java.net.URI; | |
| import java.net.http.HttpClient; | |
| import java.net.http.HttpRequest; | |
| import java.net.http.HttpResponse; | |
| import java.net.http.HttpRequest.BodyPublishers; | |
| import java.net.http.HttpResponse.BodyHandler; | |
| import java.net.http.HttpResponse.BodyHandlers; | |
| import java.nio.charset.Charset; | |
| import java.nio.charset.StandardCharsets; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import java.util.Optional; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.function.Consumer; | |
| import java.util.function.Supplier; | |
| import javax.net.ssl.SSLContext; | |
| import jdk.test.lib.util.FileUtils; | |
| import static java.lang.System.out; | |
| import static java.nio.charset.StandardCharsets.*; | |
| import static java.nio.file.StandardOpenOption.*; | |
| import org.testng.annotations.AfterTest; | |
| import org.testng.annotations.BeforeTest; | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Test; | |
| import static org.testng.Assert.*; | |
| /* | |
| * @test | |
| * @bug 8087112 | |
| * @modules java.net.http/jdk.internal.net.http.common | |
| * java.logging | |
| * jdk.httpserver | |
| * @library /test/lib /test/jdk/java/net/httpclient/lib | |
| * @compile ../../../com/sun/net/httpserver/LogFilter.java | |
| * @compile ../../../com/sun/net/httpserver/EchoHandler.java | |
| * @compile ../../../com/sun/net/httpserver/FileServerHandler.java | |
| * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestServerConfigurator | |
| * @build LightWeightHttpServer | |
| * @build jdk.test.lib.Platform | |
| * @build jdk.test.lib.util.FileUtils | |
| * @run testng/othervm RequestBodyTest | |
| */ | |
| public class RequestBodyTest { | |
| static final String fileroot = System.getProperty("test.src", ".") + "/docs"; | |
| static final String midSizedFilename = "/files/notsobigfile.txt"; | |
| static final String smallFilename = "/files/smallfile.txt"; | |
| final ConcurrentHashMap<String,Throwable> failures = new ConcurrentHashMap<>(); | |
| HttpClient client; | |
| String httpURI; | |
| String httpsURI; | |
| enum RequestBody { | |
| BYTE_ARRAY, | |
| BYTE_ARRAY_OFFSET, | |
| BYTE_ARRAYS, | |
| FILE, | |
| INPUTSTREAM, | |
| STRING, | |
| STRING_WITH_CHARSET | |
| } | |
| enum ResponseBody { | |
| BYTE_ARRAY, | |
| BYTE_ARRAY_CONSUMER, | |
| DISCARD, | |
| FILE, | |
| FILE_WITH_OPTION, | |
| STRING, | |
| STRING_WITH_CHARSET, | |
| } | |
| @BeforeTest | |
| public void setup() throws Exception { | |
| LightWeightHttpServer.initServer(); | |
| httpURI = LightWeightHttpServer.httproot + "echo/foo"; | |
| httpsURI = LightWeightHttpServer.httpsroot + "echo/foo"; | |
| SSLContext ctx = LightWeightHttpServer.ctx; | |
| client = HttpClient.newBuilder() | |
| .sslContext(ctx) | |
| .version(HttpClient.Version.HTTP_1_1) | |
| .followRedirects(HttpClient.Redirect.ALWAYS) | |
| .build(); | |
| } | |
| @AfterTest | |
| public void teardown() throws Exception { | |
| try { | |
| LightWeightHttpServer.stop(); | |
| } finally { | |
| System.out.println("RequestBodyTest: " + failures.size() + " failures"); | |
| int i = 0; | |
| for (String key: failures.keySet()) { | |
| System.out.println("test" + key + " failed: " + failures.get(key)); | |
| failures.get(key).printStackTrace(System.out); | |
| if (i++ > 3) { | |
| System.out.println("..... other failures not printed ...."); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| @DataProvider | |
| public Object[][] exchanges() throws Exception { | |
| List<Object[]> values = new ArrayList<>(); | |
| for (boolean async : new boolean[] { false, true }) | |
| for (String uri : new String[] { httpURI, httpsURI }) | |
| for (String file : new String[] { smallFilename, midSizedFilename }) | |
| for (RequestBody requestBodyType : RequestBody.values()) | |
| for (ResponseBody responseBodyType : ResponseBody.values()) | |
| for (boolean bufferResponseBody : new boolean[] { false, true }) | |
| values.add(new Object[] | |
| {uri, requestBodyType, responseBodyType, file, async, bufferResponseBody}); | |
| return values.stream().toArray(Object[][]::new); | |
| } | |
| @Test(dataProvider = "exchanges") | |
| void exchange(String target, | |
| RequestBody requestBodyType, | |
| ResponseBody responseBodyType, | |
| String file, | |
| boolean async, | |
| boolean bufferResponseBody) | |
| throws Exception | |
| { | |
| try { | |
| Path filePath = Paths.get(fileroot + file); | |
| URI uri = new URI(target); | |
| HttpRequest request = createRequest(uri, requestBodyType, filePath); | |
| checkResponse(client, request, requestBodyType, responseBodyType, filePath, async, bufferResponseBody); | |
| } catch (Exception | Error x) { | |
| Object[] params = new Object[] { | |
| target, requestBodyType, responseBodyType, | |
| file, "async=" + async, "buffer=" + bufferResponseBody | |
| }; | |
| failures.put(java.util.Arrays.toString(params), x); | |
| throw x; | |
| } | |
| } | |
| static final int DEFAULT_OFFSET = 10; | |
| static final int DEFAULT_LENGTH_FACTOR = 4/5; | |
| HttpRequest createRequest(URI uri, | |
| RequestBody requestBodyType, | |
| Path file) | |
| throws IOException | |
| { | |
| HttpRequest.Builder rb = HttpRequest.newBuilder(uri); | |
| String filename = file.toFile().getAbsolutePath(); | |
| byte[] fileAsBytes = getFileBytes(filename); | |
| String fileAsString = new String(fileAsBytes, UTF_8); | |
| switch (requestBodyType) { | |
| case BYTE_ARRAY: | |
| rb.POST(BodyPublishers.ofByteArray(fileAsBytes)); | |
| break; | |
| case BYTE_ARRAY_OFFSET: | |
| rb.POST(BodyPublishers.ofByteArray(fileAsBytes, | |
| DEFAULT_OFFSET, | |
| fileAsBytes.length * DEFAULT_LENGTH_FACTOR)); | |
| break; | |
| case BYTE_ARRAYS: | |
| Iterable<byte[]> iterable = Arrays.asList(fileAsBytes); | |
| rb.POST(BodyPublishers.ofByteArrays(iterable)); | |
| break; | |
| case FILE: | |
| rb.POST(BodyPublishers.ofFile(file)); | |
| break; | |
| case INPUTSTREAM: | |
| rb.POST(BodyPublishers.ofInputStream(fileInputStreamSupplier(file))); | |
| break; | |
| case STRING: | |
| rb.POST(BodyPublishers.ofString(fileAsString)); | |
| break; | |
| case STRING_WITH_CHARSET: | |
| rb.POST(BodyPublishers.ofString(new String(fileAsBytes), Charset.defaultCharset())); | |
| break; | |
| default: | |
| throw new AssertionError("Unknown request body:" + requestBodyType); | |
| } | |
| return rb.build(); | |
| } | |
| void checkResponse(HttpClient client, | |
| HttpRequest request, | |
| RequestBody requestBodyType, | |
| ResponseBody responseBodyType, | |
| Path file, | |
| boolean async, | |
| boolean bufferResponseBody) | |
| throws InterruptedException, IOException | |
| { | |
| String filename = file.toFile().getAbsolutePath(); | |
| byte[] fileAsBytes = getFileBytes(filename); | |
| if (requestBodyType == RequestBody.BYTE_ARRAY_OFFSET) { | |
| // Truncate the expected response body, if only a portion was sent | |
| int length = DEFAULT_OFFSET + (fileAsBytes.length * DEFAULT_LENGTH_FACTOR); | |
| fileAsBytes = Arrays.copyOfRange(fileAsBytes, DEFAULT_OFFSET, length); | |
| } | |
| String fileAsString = new String(fileAsBytes, UTF_8); | |
| Path tempFile = Paths.get("RequestBodyTest.tmp"); | |
| FileUtils.deleteFileIfExistsWithRetry(tempFile); | |
| switch (responseBodyType) { | |
| case BYTE_ARRAY: | |
| BodyHandler<byte[]> bh = BodyHandlers.ofByteArray(); | |
| if (bufferResponseBody) bh = BodyHandlers.buffering(bh, 50); | |
| HttpResponse<byte[]> bar = getResponse(client, request, bh, async); | |
| assertEquals(bar.statusCode(), 200); | |
| assertEquals(bar.body(), fileAsBytes); | |
| break; | |
| case BYTE_ARRAY_CONSUMER: | |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| Consumer<Optional<byte[]>> consumer = o -> consumerBytes(o, baos); | |
| BodyHandler<Void> bh1 = BodyHandlers.ofByteArrayConsumer(consumer); | |
| if (bufferResponseBody) bh1 = BodyHandlers.buffering(bh1, 49); | |
| HttpResponse<Void> v = getResponse(client, request, bh1, async); | |
| byte[] ba = baos.toByteArray(); | |
| assertEquals(v.statusCode(), 200); | |
| assertEquals(ba, fileAsBytes); | |
| break; | |
| case DISCARD: | |
| Object o = new Object(); | |
| BodyHandler<Object> bh2 = BodyHandlers.replacing(o); | |
| if (bufferResponseBody) bh2 = BodyHandlers.buffering(bh2, 51); | |
| HttpResponse<Object> or = getResponse(client, request, bh2, async); | |
| assertEquals(or.statusCode(), 200); | |
| assertSame(or.body(), o); | |
| break; | |
| case FILE: | |
| BodyHandler<Path> bh3 = BodyHandlers.ofFile(tempFile); | |
| if (bufferResponseBody) bh3 = BodyHandlers.buffering(bh3, 48); | |
| HttpResponse<Path> fr = getResponse(client, request, bh3, async); | |
| assertEquals(fr.statusCode(), 200); | |
| assertEquals(Files.size(tempFile), fileAsString.length()); | |
| assertEquals(Files.readAllBytes(tempFile), fileAsBytes); | |
| break; | |
| case FILE_WITH_OPTION: | |
| BodyHandler<Path> bh4 = BodyHandlers.ofFile(tempFile, CREATE_NEW, WRITE); | |
| if (bufferResponseBody) bh4 = BodyHandlers.buffering(bh4, 52); | |
| fr = getResponse(client, request, bh4, async); | |
| assertEquals(fr.statusCode(), 200); | |
| assertEquals(Files.size(tempFile), fileAsString.length()); | |
| assertEquals(Files.readAllBytes(tempFile), fileAsBytes); | |
| break; | |
| case STRING: | |
| BodyHandler<String> bh5 = BodyHandlers.ofString(); | |
| if(bufferResponseBody) bh5 = BodyHandlers.buffering(bh5, 47); | |
| HttpResponse<String> sr = getResponse(client, request, bh5, async); | |
| assertEquals(sr.statusCode(), 200); | |
| assertEquals(sr.body(), fileAsString); | |
| break; | |
| case STRING_WITH_CHARSET: | |
| BodyHandler<String> bh6 = BodyHandlers.ofString(StandardCharsets.UTF_8); | |
| if (bufferResponseBody) bh6 = BodyHandlers.buffering(bh6, 53); | |
| HttpResponse<String> r = getResponse(client, request, bh6, async); | |
| assertEquals(r.statusCode(), 200); | |
| assertEquals(r.body(), fileAsString); | |
| break; | |
| default: | |
| throw new AssertionError("Unknown response body:" + responseBodyType); | |
| } | |
| } | |
| static <T> HttpResponse<T> getResponse(HttpClient client, | |
| HttpRequest request, | |
| HttpResponse.BodyHandler<T> handler, | |
| boolean async) | |
| throws InterruptedException, IOException | |
| { | |
| if (!async) | |
| return client.send(request, handler); | |
| else | |
| return client.sendAsync(request, handler).join(); | |
| } | |
| static byte[] getFileBytes(String path) throws IOException { | |
| try (FileInputStream fis = new FileInputStream(path); | |
| BufferedInputStream bis = new BufferedInputStream(fis); | |
| ByteArrayOutputStream baos = new ByteArrayOutputStream()) { | |
| bis.transferTo(baos); | |
| return baos.toByteArray(); | |
| } | |
| } | |
| static Supplier<FileInputStream> fileInputStreamSupplier(Path f) { | |
| return new Supplier<>() { | |
| Path file = f; | |
| @Override | |
| public FileInputStream get() { | |
| try { | |
| return new FileInputStream(file.toFile()); | |
| } catch (IOException x) { | |
| throw new UncheckedIOException(x); | |
| } | |
| } | |
| }; | |
| } | |
| static void consumerBytes(Optional<byte[]> bytes, ByteArrayOutputStream baos) { | |
| try { | |
| if (bytes.isPresent()) | |
| baos.write(bytes.get()); | |
| } catch (IOException x) { | |
| throw new UncheckedIOException(x); | |
| } | |
| } | |
| // --- | |
| /* Main entry point for standalone testing of the main functional test. */ | |
| public static void main(String... args) throws Exception { | |
| RequestBodyTest t = new RequestBodyTest(); | |
| t.setup(); | |
| int count = 0; | |
| try { | |
| for (Object[] objs : t.exchanges()) { | |
| count++; | |
| out.printf("********* iteration: %d %s %s %s %s %s %s *********%n", | |
| count, objs[0], objs[1], objs[2], objs[3], objs[4], objs[5]); | |
| t.exchange((String) objs[0], | |
| (RequestBody) objs[1], | |
| (ResponseBody) objs[2], | |
| (String) objs[3], | |
| (boolean) objs[4], | |
| (boolean) objs[5]); | |
| } | |
| } finally { | |
| t.teardown(); | |
| } | |
| } | |
| } |