db85090553
Co-authored-by: Sean Mullan <mullan@openjdk.org> Co-authored-by: Alan Bateman <alanb@openjdk.org> Co-authored-by: Weijun Wang <weijun@openjdk.org> Co-authored-by: Aleksei Efimov <aefimov@openjdk.org> Co-authored-by: Brian Burkhalter <bpb@openjdk.org> Co-authored-by: Daniel Fuchs <dfuchs@openjdk.org> Co-authored-by: Harshitha Onkar <honkar@openjdk.org> Co-authored-by: Joe Wang <joehw@openjdk.org> Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Justin Lu <jlu@openjdk.org> Co-authored-by: Kevin Walls <kevinw@openjdk.org> Co-authored-by: Lance Andersen <lancea@openjdk.org> Co-authored-by: Naoto Sato <naoto@openjdk.org> Co-authored-by: Roger Riggs <rriggs@openjdk.org> Co-authored-by: Brent Christian <bchristi@openjdk.org> Co-authored-by: Stuart Marks <smarks@openjdk.org> Co-authored-by: Ian Graves <igraves@openjdk.org> Co-authored-by: Phil Race <prr@openjdk.org> Co-authored-by: Erik Gahlin <egahlin@openjdk.org> Co-authored-by: Jaikiran Pai <jpai@openjdk.org> Reviewed-by: kevinw, aivanov, rriggs, lancea, coffeys, dfuchs, ihse, erikj, cjplummer, coleenp, naoto, mchung, prr, weijun, joehw, azvegint, psadhukhan, bchristi, sundar, attila
371 lines
15 KiB
Java
371 lines
15 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|
|
}
|