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
397
test/jdk/java/net/httpclient/http3/H3ServerPush.java
Normal file
397
test/jdk/java/net/httpclient/http3/H3ServerPush.java
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8087112 8159814
|
||||
* @library /test/lib /test/jdk/java/net/httpclient/lib
|
||||
* @build jdk.httpclient.test.lib.http2.Http2TestServer
|
||||
* jdk.httpclient.test.lib.http2.PushHandler
|
||||
* jdk.test.lib.Utils
|
||||
* jdk.test.lib.net.SimpleSSLContext
|
||||
* @run junit/othervm/timeout=960
|
||||
* -Djdk.httpclient.HttpClient.log=errors,requests,headers
|
||||
* -Djdk.internal.httpclient.debug=false
|
||||
* H3ServerPush
|
||||
* @summary This is a clone of http2/ServerPush but for HTTP/3
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Builder;
|
||||
import java.net.http.HttpClient.Version;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpResponse.BodyHandler;
|
||||
import java.net.http.HttpResponse.BodyHandlers;
|
||||
import java.net.http.HttpResponse.BodySubscribers;
|
||||
import java.net.http.HttpResponse.PushPromiseHandler;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.httpclient.test.lib.common.HttpServerAdapters;
|
||||
import jdk.httpclient.test.lib.http2.Http2TestServer;
|
||||
import jdk.httpclient.test.lib.http2.PushHandler;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static jdk.test.lib.Utils.createTempFileOfSize;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class H3ServerPush implements HttpServerAdapters {
|
||||
|
||||
private static final String CLASS_NAME = H3ServerPush.class.getSimpleName();
|
||||
|
||||
static final int LOOPS = 13;
|
||||
static final int FILE_SIZE = 512 * 1024 + 343;
|
||||
|
||||
private static Path tempFile;
|
||||
|
||||
private static HttpTestServer server;
|
||||
private static URI uri;
|
||||
private static URI headURI;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws Exception {
|
||||
tempFile = createTempFileOfSize(CLASS_NAME, ".dat", FILE_SIZE);
|
||||
var sslContext = SimpleSSLContext.findSSLContext();
|
||||
var h2Server = new Http2TestServer(true, sslContext);
|
||||
h2Server.enableH3AltServiceOnSamePort();
|
||||
h2Server.addHandler(new PushHandler(tempFile, LOOPS), "/foo/");
|
||||
System.out.println("Using temp file:" + tempFile);
|
||||
server = HttpTestServer.of(h2Server);
|
||||
server.addHandler(new HttpHeadOrGetHandler(), "/head/");
|
||||
headURI = new URI("https://" + server.serverAuthority() + "/head/x");
|
||||
uri = new URI("https://" + server.serverAuthority() + "/foo/a/b/c");
|
||||
|
||||
System.err.println("Server listening at " + server.serverAuthority());
|
||||
server.start();
|
||||
|
||||
}
|
||||
|
||||
private void sendHeadRequest(HttpClient client) throws IOException, InterruptedException {
|
||||
HttpRequest headRequest = HttpRequest.newBuilder(headURI)
|
||||
.HEAD().version(Version.HTTP_2).build();
|
||||
var headResponse = client.send(headRequest, BodyHandlers.ofString());
|
||||
assertEquals(200, headResponse.statusCode());
|
||||
assertEquals(Version.HTTP_2, headResponse.version());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void teardown() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
// Test 1 - custom written push promise handler, everything as a String
|
||||
@Test
|
||||
public void testTypeString() throws Exception {
|
||||
System.out.println("\n**** testTypeString\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<String>>>
|
||||
resultMap = new ConcurrentHashMap<>();
|
||||
|
||||
PushPromiseHandler<String> pph = (initial, pushRequest, acceptor) -> {
|
||||
BodyHandler<String> s = BodyHandlers.ofString(UTF_8);
|
||||
CompletableFuture<HttpResponse<String>> cf = acceptor.apply(s);
|
||||
resultMap.put(pushRequest, cf);
|
||||
};
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET()
|
||||
.build();
|
||||
CompletableFuture<HttpResponse<String>> cf =
|
||||
client.sendAsync(request, BodyHandlers.ofString(UTF_8), pph);
|
||||
resultMap.put(request, cf);
|
||||
System.out.println("waiting for response");
|
||||
var resp = cf.join();
|
||||
assertEquals(Version.HTTP_3, resp.version());
|
||||
var seen = new HashSet<>();
|
||||
resultMap.forEach((k, v) -> {
|
||||
if (seen.add(k)) {
|
||||
System.out.println("Got " + v.join());
|
||||
}
|
||||
});
|
||||
|
||||
// waiting for all promises to reach us
|
||||
System.out.println("waiting for promises");
|
||||
System.out.println("results.size: " + resultMap.size());
|
||||
for (HttpRequest r : resultMap.keySet()) {
|
||||
System.out.println("Checking " + r);
|
||||
HttpResponse<String> response = resultMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
assertEquals(tempFileAsString, response.body());
|
||||
}
|
||||
resultMap.forEach((k, v) -> {
|
||||
if (seen.add(k)) {
|
||||
System.out.println("Got " + v.join());
|
||||
}
|
||||
});
|
||||
assertEquals(LOOPS + 1, resultMap.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2 - of(...) populating the given Map, everything as a String
|
||||
@Test
|
||||
public void testTypeStringOfMap() throws Exception {
|
||||
System.out.println("\n**** testTypeStringOfMap\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<String>>>
|
||||
resultMap = new ConcurrentHashMap<>();
|
||||
|
||||
PushPromiseHandler<String> pph =
|
||||
PushPromiseHandler.of(pushPromise -> BodyHandlers.ofString(UTF_8), resultMap);
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
|
||||
CompletableFuture<HttpResponse<String>> cf =
|
||||
client.sendAsync(request, BodyHandlers.ofString(UTF_8), pph);
|
||||
cf.join();
|
||||
resultMap.put(request, cf);
|
||||
System.err.println("results.size: " + resultMap.size());
|
||||
for (HttpRequest r : resultMap.keySet()) {
|
||||
HttpResponse<String> response = resultMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
assertEquals(tempFileAsString, response.body());
|
||||
}
|
||||
assertEquals(LOOPS + 1, resultMap.size());
|
||||
}
|
||||
}
|
||||
|
||||
// --- Path ---
|
||||
|
||||
static final Path dir = Paths.get(".", "serverPush");
|
||||
static BodyHandler<Path> requestToPath(HttpRequest req) {
|
||||
URI u = req.uri();
|
||||
Path path = Paths.get(dir.toString(), u.getPath());
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
} catch (IOException ee) {
|
||||
throw new UncheckedIOException(ee);
|
||||
}
|
||||
return BodyHandlers.ofFile(path);
|
||||
}
|
||||
|
||||
// Test 3 - custom written push promise handler, everything as a Path
|
||||
@Test
|
||||
public void testTypePath() throws Exception {
|
||||
System.out.println("\n**** testTypePath\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Path>>> resultsMap
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
PushPromiseHandler<Path> pushPromiseHandler = (initial, pushRequest, acceptor) -> {
|
||||
BodyHandler<Path> pp = requestToPath(pushRequest);
|
||||
CompletableFuture<HttpResponse<Path>> cf = acceptor.apply(pp);
|
||||
resultsMap.put(pushRequest, cf);
|
||||
};
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
|
||||
CompletableFuture<HttpResponse<Path>> cf =
|
||||
client.sendAsync(request, requestToPath(request), pushPromiseHandler);
|
||||
cf.join();
|
||||
resultsMap.put(request, cf);
|
||||
for (HttpRequest r : resultsMap.keySet()) {
|
||||
HttpResponse<Path> response = resultsMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
String fileAsString = Files.readString(response.body());
|
||||
assertEquals(tempFileAsString, fileAsString);
|
||||
}
|
||||
assertEquals(LOOPS + 1, resultsMap.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4 - of(...) populating the given Map, everything as a Path
|
||||
@Test
|
||||
public void testTypePathOfMap() throws Exception {
|
||||
System.out.println("\n**** testTypePathOfMap\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Path>>> resultsMap
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
PushPromiseHandler<Path> pushPromiseHandler =
|
||||
PushPromiseHandler.of(H3ServerPush::requestToPath, resultsMap);
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
|
||||
CompletableFuture<HttpResponse<Path>> cf =
|
||||
client.sendAsync(request, requestToPath(request), pushPromiseHandler);
|
||||
cf.join();
|
||||
resultsMap.put(request, cf);
|
||||
for (HttpRequest r : resultsMap.keySet()) {
|
||||
HttpResponse<Path> response = resultsMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
String fileAsString = Files.readString(response.body());
|
||||
assertEquals(tempFileAsString, fileAsString);
|
||||
}
|
||||
assertEquals(LOOPS + 1, resultsMap.size());
|
||||
}
|
||||
}
|
||||
|
||||
// --- Consumer<byte[]> ---
|
||||
|
||||
static class ByteArrayConsumer implements Consumer<Optional<byte[]>> {
|
||||
volatile List<byte[]> listByteArrays = new ArrayList<>();
|
||||
volatile byte[] accumulatedBytes;
|
||||
|
||||
public byte[] getAccumulatedBytes() { return accumulatedBytes; }
|
||||
|
||||
@Override
|
||||
public void accept(Optional<byte[]> optionalBytes) {
|
||||
assert accumulatedBytes == null;
|
||||
if (optionalBytes.isEmpty()) {
|
||||
int size = listByteArrays.stream().mapToInt(ba -> ba.length).sum();
|
||||
ByteBuffer bb = ByteBuffer.allocate(size);
|
||||
listByteArrays.forEach(bb::put);
|
||||
accumulatedBytes = bb.array();
|
||||
} else {
|
||||
listByteArrays.add(optionalBytes.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test 5 - custom written handler, everything as a consumer of optional byte[]
|
||||
@Test
|
||||
public void testTypeByteArrayConsumer() throws Exception {
|
||||
System.out.println("\n**** testTypeByteArrayConsumer\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Void>>> resultsMap
|
||||
= new ConcurrentHashMap<>();
|
||||
Map<HttpRequest,ByteArrayConsumer> byteArrayConsumerMap
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
|
||||
ByteArrayConsumer bac = new ByteArrayConsumer();
|
||||
byteArrayConsumerMap.put(request, bac);
|
||||
|
||||
PushPromiseHandler<Void> pushPromiseHandler = (initial, pushRequest, acceptor) -> {
|
||||
CompletableFuture<HttpResponse<Void>> cf = acceptor.apply(
|
||||
(info) -> {
|
||||
ByteArrayConsumer bc = new ByteArrayConsumer();
|
||||
byteArrayConsumerMap.put(pushRequest, bc);
|
||||
return BodySubscribers.ofByteArrayConsumer(bc);
|
||||
});
|
||||
resultsMap.put(pushRequest, cf);
|
||||
};
|
||||
|
||||
CompletableFuture<HttpResponse<Void>> cf =
|
||||
client.sendAsync(request, BodyHandlers.ofByteArrayConsumer(bac), pushPromiseHandler);
|
||||
cf.join();
|
||||
resultsMap.put(request, cf);
|
||||
for (HttpRequest r : resultsMap.keySet()) {
|
||||
HttpResponse<Void> response = resultsMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes();
|
||||
String result = new String(ba, UTF_8);
|
||||
assertEquals(tempFileAsString, result);
|
||||
}
|
||||
assertEquals(LOOPS + 1, resultsMap.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Test 6 - of(...) populating the given Map, everything as a consumer of optional byte[]
|
||||
@Test
|
||||
public void testTypeByteArrayConsumerOfMap() throws Exception {
|
||||
System.out.println("\n**** testTypeByteArrayConsumerOfMap\n");
|
||||
String tempFileAsString = Files.readString(tempFile);
|
||||
ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Void>>> resultsMap
|
||||
= new ConcurrentHashMap<>();
|
||||
Map<HttpRequest, ByteArrayConsumer> byteArrayConsumerMap
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
try (HttpClient client = newClientBuilderForH3().proxy(Builder.NO_PROXY)
|
||||
.sslContext(SimpleSSLContext.findSSLContext())
|
||||
.version(Version.HTTP_3).build()) {
|
||||
sendHeadRequest(client);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
|
||||
ByteArrayConsumer bac = new ByteArrayConsumer();
|
||||
byteArrayConsumerMap.put(request, bac);
|
||||
|
||||
PushPromiseHandler<Void> pushPromiseHandler =
|
||||
PushPromiseHandler.of(
|
||||
pushRequest -> {
|
||||
ByteArrayConsumer bc = new ByteArrayConsumer();
|
||||
byteArrayConsumerMap.put(pushRequest, bc);
|
||||
return BodyHandlers.ofByteArrayConsumer(bc);
|
||||
},
|
||||
resultsMap);
|
||||
|
||||
CompletableFuture<HttpResponse<Void>> cf =
|
||||
client.sendAsync(request, BodyHandlers.ofByteArrayConsumer(bac), pushPromiseHandler);
|
||||
cf.join();
|
||||
resultsMap.put(request, cf);
|
||||
for (HttpRequest r : resultsMap.keySet()) {
|
||||
HttpResponse<Void> response = resultsMap.get(r).join();
|
||||
assertEquals(200, response.statusCode());
|
||||
assertEquals(Version.HTTP_3, response.version());
|
||||
byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes();
|
||||
String result = new String(ba, UTF_8);
|
||||
assertEquals(tempFileAsString, result);
|
||||
}
|
||||
assertEquals(LOOPS + 1, resultsMap.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue