undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
365
test/jdk/java/net/httpclient/RequestBodyTest.java
Normal file
365
test/jdk/java/net/httpclient/RequestBodyTest.java
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2026, 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.junit.jupiter.api.AfterAll;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8087112
|
||||
* @library /test/lib /test/jdk/java/net/httpclient/lib
|
||||
* @build jdk.test.lib.net.SimpleSSLContext
|
||||
* jdk.httpclient.test.lib.common.HttpServerAdapters
|
||||
* @build LightWeightHttpServer
|
||||
* @build jdk.test.lib.Platform
|
||||
* @build jdk.test.lib.util.FileUtils
|
||||
* @run junit/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";
|
||||
private static final ConcurrentHashMap<String,Throwable> failures = new ConcurrentHashMap<>();
|
||||
|
||||
private static HttpClient client;
|
||||
private static String httpURI;
|
||||
private static 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,
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static 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();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static 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);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("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(200, bar.statusCode());
|
||||
assertArrayEquals(fileAsBytes, bar.body());
|
||||
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(200, v.statusCode());
|
||||
assertArrayEquals(fileAsBytes, ba);
|
||||
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(200, or.statusCode());
|
||||
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(200, fr.statusCode());
|
||||
assertEquals(fileAsString.length(), Files.size(tempFile));
|
||||
assertArrayEquals(fileAsBytes, Files.readAllBytes(tempFile));
|
||||
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(200, fr.statusCode());
|
||||
assertEquals(fileAsString.length(), Files.size(tempFile));
|
||||
assertArrayEquals(fileAsBytes, Files.readAllBytes(tempFile));
|
||||
break;
|
||||
case STRING:
|
||||
BodyHandler<String> bh5 = BodyHandlers.ofString();
|
||||
if(bufferResponseBody) bh5 = BodyHandlers.buffering(bh5, 47);
|
||||
HttpResponse<String> sr = getResponse(client, request, bh5, async);
|
||||
assertEquals(200, sr.statusCode());
|
||||
assertEquals(fileAsString, sr.body());
|
||||
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(200, r.statusCode());
|
||||
assertEquals(fileAsString, r.body());
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue