8368528: HttpClient.Builder.connectTimeout should accept arbitrarily large values

Reviewed-by: dfuchs
This commit is contained in:
Volkan Yazici 2025-11-06 06:22:32 +00:00
parent 3f40f4c362
commit c754e3e095
5 changed files with 621 additions and 57 deletions

View File

@ -499,22 +499,28 @@ abstract class HttpQuicConnection extends HttpConnection {
}, exchange.parentExecutor.safeDelegate());
}
Optional<Duration> timeout = client().connectTimeout();
CompletableFuture<CompletableFuture<Void>> fxi = handshakeCfCf;
// In case of connection timeout, set up a timeout on the handshakeCfCf.
// Note: this is a different timeout than the direct connection timeout.
if (timeout.isPresent()) {
Duration timeout = client().connectTimeout().orElse(null);
if (timeout != null) {
// In case of timeout we need to close the quic connection
debug.log("setting up quic connect timeout: " + timeout.get().toMillis());
debug.log("setting up quic connect timeout: " + timeout);
long timeoutMillis;
try {
timeoutMillis = timeout.toMillis();
} catch (ArithmeticException _) {
timeoutMillis = Long.MAX_VALUE;
}
fxi = handshakeCfCf.completeOnTimeout(
MinimalFuture.failedFuture(new HttpConnectTimeoutException("quic connect timeout")),
timeout.get().toMillis(), TimeUnit.MILLISECONDS);
timeoutMillis, TimeUnit.MILLISECONDS);
}
// If we have set up any timeout, arrange to close the quicConnection
// if one of the timeout expires
if (timeout.isPresent() || directTimeout.isPresent()) {
if (timeout != null || directTimeout.isPresent()) {
fxi = fxi.handleAsync(this::handleTimeout, exchange.parentExecutor.safeDelegate());
}
return fxi.thenCompose(Function.identity());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -28,14 +28,22 @@ import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
/**
* A Deadline represents an instant on a {@linkplain TimeLine time line}.
* An instantaneous point on the {@linkplain TimeLine time-line}.
* <p>
* This class is immutable and thread-safe.
* <p id="overflow">
* Operations that add or subtract durations to a {@code Deadline}, whether
* represented as a {@link Duration} or as a {@code long} time increment (such
* as seconds or nanoseconds) do not throw on numeric overflow if the resulting
* {@code Deadline} would exceed {@link #MAX} or be less than {@link #MIN}.
* Instead, {@code MAX} or {@code MIN} is returned, respectively. Similarly,
* methods that return a duration as a {@code long} will either return
* {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE} if the returned quantity
* would exceed the capacity of a {@code long}.
*/
public final class Deadline implements Comparable<Deadline> {
@ -49,17 +57,24 @@ public final class Deadline implements Comparable<Deadline> {
/**
* Returns a copy of this deadline with the specified duration in nanoseconds added.
* {@return a deadline with the specified duration in nanoseconds added}
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MAX} if the provided duration is positive,
* {@link Deadline#MIN} otherwise.
*
* @param nanosToAdd the nanoseconds to add, positive or negative
* @return a {@code Deadline} based on this deadline with the specified nanoseconds added, not null
* @throws DateTimeException if the result exceeds the maximum or minimum deadline
* @throws ArithmeticException if numeric overflow occurs
*/
public Deadline plusNanos(long nanosToAdd) {
return new Deadline(deadline.plusNanos(nanosToAdd));
if (nanosToAdd == 0) return this;
try {
return new Deadline(deadline.plusNanos(nanosToAdd));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return nanosToAdd > 0 ? Deadline.MAX : Deadline.MIN;
}
}
/**
@ -89,92 +104,116 @@ public final class Deadline implements Comparable<Deadline> {
}
/**
* Returns a copy of this deadline with the specified amount subtracted.
* <p>
* This returns a {@code Deadline}, based on this one, with the specified amount subtracted.
* The amount is typically {@link Duration} but may be any other type implementing
* the {@link TemporalAmount} interface.
* {@return a deadline with the specified amount subtracted from this deadline}
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MIN} if the provided duration is positive,
* {@link Deadline#MAX} otherwise.
*
* @param amountToSubtract the amount to subtract, not null
* @return a {@code Deadline} based on this deadline with the subtraction made, not null
* @throws DateTimeException if the subtraction cannot be made
* @throws ArithmeticException if numeric overflow occurs
* @param duration the amount to subtract, not null
*/
public Deadline minus(TemporalAmount amountToSubtract) {
return Deadline.of(deadline.minus(amountToSubtract));
public Deadline minus(Duration duration) {
if (duration.isZero()) return this;
try {
return Deadline.of(deadline.minus(duration));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return duration.isPositive() ? Deadline.MIN : Deadline.MAX;
}
}
/**
* Returns a copy of this deadline with the specified amount added.
* {@return a deadline with the specified amount added to this deadline}
* <p>
* This returns a {@code Deadline}, based on this one, with the amount
* in terms of the unit added. If it is not possible to add the amount, because the
* unit is not supported or for some other reason, an exception is thrown.
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MAX} if the provided amount is positive,
* {@link Deadline#MIN} otherwise.
*
* @see Instant#plus(long, TemporalUnit)
*
* @param amountToAdd the amount of the unit to add to the result, may be negative
* @param unit the unit of the amount to add, not null
* @return a {@code Deadline} based on this deadline with the specified amount added, not null
* @throws DateTimeException if the addition cannot be made
* @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
public Deadline plus(long amountToAdd, TemporalUnit unit) {
if (amountToAdd == 0) return this;
return Deadline.of(deadline.plus(amountToAdd, unit));
try {
return Deadline.of(deadline.plus(amountToAdd, unit));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return amountToAdd > 0 ? Deadline.MAX : Deadline.MIN;
}
}
/**
* Returns a copy of this deadline with the specified duration in seconds added.
* {@return a deadline with the specified duration in seconds added to this deadline}
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MAX} if the provided duration is positive,
* {@link Deadline#MIN} otherwise.
*
* @param secondsToAdd the seconds to add, positive or negative
* @return a {@code Deadline} based on this deadline with the specified seconds added, not null
* @throws DateTimeException if the result exceeds the maximum or minimum deadline
* @throws ArithmeticException if numeric overflow occurs
*/
public Deadline plusSeconds(long secondsToAdd) {
if (secondsToAdd == 0) return this;
return Deadline.of(deadline.plusSeconds(secondsToAdd));
try {
return Deadline.of(deadline.plusSeconds(secondsToAdd));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return secondsToAdd > 0 ? Deadline.MAX : Deadline.MIN;
}
}
/**
* Returns a copy of this deadline with the specified duration in milliseconds added.
* {@return a deadline with the specified duration in milliseconds added to this deadline}
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MAX} if the provided duration is positive,
* {@link Deadline#MIN} otherwise.
*
* @param millisToAdd the milliseconds to add, positive or negative
* @return a {@code Deadline} based on this deadline with the specified milliseconds added, not null
* @throws DateTimeException if the result exceeds the maximum or minimum deadline
* @throws ArithmeticException if numeric overflow occurs
*/
public Deadline plusMillis(long millisToAdd) {
if (millisToAdd == 0) return this;
return Deadline.of(deadline.plusMillis(millisToAdd));
try {
return Deadline.of(deadline.plusMillis(millisToAdd));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return millisToAdd > 0 ? Deadline.MAX : Deadline.MIN;
}
}
/**
* Returns a copy of this deadline with the specified amount added.
* <p>
* This returns a {@code Deadline}, based on this one, with the specified amount added.
* The amount is typically {@link Duration} but may be any other type implementing
* the {@link TemporalAmount} interface.
* {@return a deadline with the specified duration added to this deadline}
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Deadline#MAX} if the provided duration is positive,
* {@link Deadline#MIN} otherwise.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Deadline} based on this deadline with the addition made, not null
* @throws DateTimeException if the addition cannot be made
* @throws ArithmeticException if numeric overflow occurs
* @param duration the duration to add, not null
*/
public Deadline plus(TemporalAmount amountToAdd) {
return Deadline.of(deadline.plus(amountToAdd));
public Deadline plus(Duration duration) {
if (duration.isZero()) return this;
try {
return Deadline.of(deadline.plus(duration));
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
return duration.isPositive() ? Deadline.MAX : Deadline.MIN;
}
}
/**
@ -188,16 +227,24 @@ public final class Deadline implements Comparable<Deadline> {
* complete units between the two deadlines.
* <p>
* This instance is immutable and unaffected by this method call.
* <p>
* On {@linkplain ##overflow numeric overflows}, this method will return
* {@link Long#MAX_VALUE} if the current deadline is before the provided end
* deadline, {@link Long#MIN_VALUE} otherwise.
*
* @param endExclusive the end deadline, exclusive
* @param unit the unit to measure the amount in, not null
* @return the amount of time between this deadline and the end deadline
* @throws DateTimeException if the amount cannot be calculated
* @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
public long until(Deadline endExclusive, TemporalUnit unit) {
return deadline.until(endExclusive.deadline, unit);
try {
return deadline.until(endExclusive.deadline, unit);
} catch (DateTimeException | // "Instant exceeds minimum or maximum instant"
ArithmeticException _) { // "long overflow"
int delta = compareTo(endExclusive);
return delta < 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
}
}
/**
@ -266,10 +313,13 @@ public final class Deadline implements Comparable<Deadline> {
* @param startInclusive the start deadline, inclusive, not null
* @param endExclusive the end deadline, exclusive, not null
* @return a {@code Duration}, not null
* @throws DateTimeException if the seconds between the deadline cannot be obtained
* @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration}
*/
public static Duration between(Deadline startInclusive, Deadline endExclusive) {
if (startInclusive.equals(endExclusive)) return Duration.ZERO;
// `Deadline` works with `Instant` under the hood.
// Delta between `Instant.MIN` and `Instant.MAX` fits in a `Duration`.
// Hence, we should never receive a numeric overflow while calculating the delta between two deadlines.
return Duration.between(startInclusive.deadline, endExclusive.deadline);
}
}

View File

@ -0,0 +1,349 @@
/*
* Copyright (c) 2025, 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 jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestEchoHandler;
import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer;
import jdk.internal.net.http.common.Logger;
import jdk.internal.net.http.common.Utils;
import jdk.test.lib.net.SimpleSSLContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpOption;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static jdk.httpclient.test.lib.common.HttpServerAdapters.createClientBuilderFor;
/*
* @test id=withoutPropertyConfig
* @bug 8368528
* @summary Verifies that `Duration`-accepting programmatic public APIs, either
* individually, or in combination, work with arbitrarily large values
*
* @library /test/jdk/java/net/httpclient/lib
* /test/lib
*
* @run junit DurationOverflowTest
*/
/*
* @test id=withPropertyConfig
* @bug 8368528
* @summary Verifies that `Duration`-accepting programmatic public APIs, either
* individually, or in combination, work with arbitrarily large values
* when combined with duration-accepting property-based public APIs.
*
* @library /test/jdk/java/net/httpclient/lib
* /test/lib
*
* @comment 9223372036854775807 is the value of `Long.MAX_VALUE`
*
* @run junit/othervm
* -Djdk.httpclient.keepalive.timeout=9223372036854775807
* DurationOverflowTest
*
* @comment `h3` infra is also enabled for this test since `j.h.k.timeout.h3`
* defaults to `j.h.k.timeout.h2`
* @run junit/othervm
* -Djdk.httpclient.keepalive.timeout.h2=9223372036854775807
* -DallowedInfras=h2,h2s,h3
* DurationOverflowTest
*
* @run junit/othervm
* -Djdk.httpclient.keepalive.timeout.h3=9223372036854775807
* -DallowedInfras=h3
* DurationOverflowTest
*/
public class DurationOverflowTest {
private static final String CLASS_NAME = DurationOverflowTest.class.getSimpleName();
private static final Logger LOGGER = Utils.getDebugLogger(CLASS_NAME::toString, Utils.DEBUG);
private static final SSLContext SSL_CONTEXT = createSslContext();
private static SSLContext createSslContext() {
try {
return new SimpleSSLContext().get();
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}
private static final List<Infra> INFRAS = loadInfras();
private static final class Infra implements AutoCloseable {
private static final AtomicInteger SERVER_COUNTER = new AtomicInteger();
private final String serverId;
private final HttpTestServer server;
private final Supplier<HttpClient.Builder> clientBuilderSupplier;
private final Supplier<HttpRequest.Builder> requestBuilderSupplier;
private final boolean secure;
private Infra(
String serverId,
HttpTestServer server,
Supplier<HttpClient.Builder> clientBuilderSupplier,
Supplier<HttpRequest.Builder> requestBuilderSupplier,
boolean secure) {
this.serverId = serverId;
this.server = server;
this.clientBuilderSupplier = clientBuilderSupplier;
this.requestBuilderSupplier = requestBuilderSupplier;
this.secure = secure;
}
private static Infra of(Version version, boolean secure) {
// Create the server and the request URI
var sslContext = secure ? SSL_CONTEXT : null;
var server = createServer(version, sslContext);
server.getVersion();
var handlerPath = "/%s/".formatted(CLASS_NAME);
var requestUriScheme = secure ? "https" : "http";
var requestUri = URI.create("%s://%s%s-".formatted(requestUriScheme, server.serverAuthority(), handlerPath));
// Register the request handler
var serverId = "" + SERVER_COUNTER.getAndIncrement();
server.addHandler(
// Intentionally opting for receiving a body to cover code paths associated with its retrieval
new HttpTestEchoHandler(false),
handlerPath);
// Create client & request builders
Supplier<HttpClient.Builder> clientBuilderSupplier =
() -> createClientBuilderFor(version)
.version(version)
.sslContext(SSL_CONTEXT)
.proxy(NO_PROXY);
Supplier<HttpRequest.Builder> requestBuilderSupplier =
() -> createRequestBuilder(requestUri, version);
// Create the pair
var pair = new Infra(serverId, server, clientBuilderSupplier, requestBuilderSupplier, secure);
pair.server.start();
LOGGER.log("Server[%s] is started at `%s`", serverId, server.serverAuthority());
return pair;
}
private static HttpTestServer createServer(Version version, SSLContext sslContext) {
try {
return switch (version) {
case HTTP_1_1, HTTP_2 -> HttpTestServer.create(version, sslContext, null);
case HTTP_3 -> HttpTestServer.create(HTTP_3_URI_ONLY, sslContext, null);
};
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}
private static HttpRequest.Builder createRequestBuilder(URI uri, Version version) {
var requestBuilder = HttpRequest.newBuilder(uri).version(version).HEAD();
if (Version.HTTP_3.equals(version)) {
requestBuilder.setOption(HttpOption.H3_DISCOVERY, HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY);
}
return requestBuilder;
}
@Override
public void close() {
LOGGER.log("Server[%s] is stopping", serverId);
server.stop();
}
@Override
public String toString() {
var version = server.getVersion();
var versionString = version.toString();
return switch (version) {
case HTTP_1_1, HTTP_2 -> secure ? versionString.replaceFirst("_", "S_") : versionString;
case HTTP_3 -> versionString;
};
}
}
private static List<Infra> loadInfras() {
return Stream
.of(System.getProperty("allowedInfras", "h1,h1s,h2,h2s,h3").split(","))
.map(infra -> {
LOGGER.log("Loading test infrastructure: `%s`", infra);
return switch (infra) {
case "h1" -> Infra.of(Version.HTTP_1_1, false);
case "h1s" -> Infra.of(Version.HTTP_1_1, true);
case "h2" -> Infra.of(Version.HTTP_2, false);
case "h2s" -> Infra.of(Version.HTTP_2, true);
case "h3" -> Infra.of(Version.HTTP_3, true);
default -> throw new IllegalArgumentException("Unknown test infrastructure: " + infra);
};
})
.toList();
}
@AfterAll
static void tearDownInfras() {
LOGGER.log("Tearing down test infrastructure");
Exception[] exceptionRef = {null};
infras().forEach(infra -> {
try {
infra.close();
} catch (Exception exception) {
if (exceptionRef[0] == null) {
exceptionRef[0] = exception;
} else {
exceptionRef[0].addSuppressed(exception);
}
}
});
if (exceptionRef[0] != null) {
throw new RuntimeException("Failed tearing down one or more test infrastructures", exceptionRef[0]);
}
}
private static Stream<Infra> infras() {
return INFRAS.stream();
}
public static final Set<Duration> EXCESSIVE_DURATIONS = Set.of(
Duration.MAX,
// This triggers different exceptions than the ones triggered by `Duration.MAX`
Duration.ofMillis(Long.MAX_VALUE));
private static Stream<InfraDurationPair> infraDurationPairs() {
return infras().flatMap(infra -> EXCESSIVE_DURATIONS.stream()
.map(duration -> new InfraDurationPair(infra, duration)));
}
private record InfraDurationPair(Infra infra, Duration duration) {}
@ParameterizedTest
@MethodSource("infraDurationPairs")
void testClientConnectTimeout(InfraDurationPair pair) throws Exception {
testConfig(pair.infra, clientBuilder -> clientBuilder.connectTimeout(pair.duration), null);
}
@ParameterizedTest
@MethodSource("infraDurationPairs")
void testRequestTimeout(InfraDurationPair pair) throws Exception {
testConfig(pair.infra, null, requestBuilder -> requestBuilder.timeout(pair.duration));
}
private static Stream<InfraDurationDurationTriple> infraDurationDurationTriples() {
return infras().flatMap(infra -> EXCESSIVE_DURATIONS.stream()
.flatMap(duration1 -> EXCESSIVE_DURATIONS.stream()
.map(duration2 -> new InfraDurationDurationTriple(infra, duration1, duration2))));
}
private record InfraDurationDurationTriple(Infra infra, Duration duration1, Duration duration2) {}
@ParameterizedTest
@MethodSource("infraDurationDurationTriples")
void testClientConnectTimeoutAndRequestTimeout(InfraDurationDurationTriple triple) throws Exception {
testConfig(
triple.infra,
clientBuilder -> clientBuilder.connectTimeout(triple.duration1),
requestBuilder -> requestBuilder.timeout(triple.duration2));
}
private static void testConfig(
Infra infra,
Consumer<HttpClient.Builder> clientBuilderConsumer,
Consumer<HttpRequest.Builder> requestBuilderConsumer)
throws Exception {
// Create the client
var clientBuilder = infra.clientBuilderSupplier.get();
if (clientBuilderConsumer != null) {
clientBuilderConsumer.accept(clientBuilder);
}
try (var client = clientBuilder.build()) {
// Create the request
byte[] expectedBytes = "abc".repeat(8192).getBytes(US_ASCII);
var requestBuilder = infra.requestBuilderSupplier.get()
// Intentionally opting for sending a body to cover code paths associated with its delivery
.POST(BodyPublishers.ofByteArray(expectedBytes));
if (requestBuilderConsumer != null) {
requestBuilderConsumer.accept(requestBuilder);
}
var request = requestBuilder.build();
// Execute the request.
// Doing it twice to touch code paths before & after a protocol upgrade, if present.
for (int requestIndex = 0; requestIndex < 2; requestIndex++) {
LOGGER.log("Executing request (attempt=%s)", requestIndex + 1);
var response = client.send(request, BodyHandlers.ofByteArray());
// Verify the response status code
if (response.statusCode() != 200) {
var message = String.format(
"Unexpected status code: %s (attempt=%s)",
response.statusCode(), requestIndex + 1);
throw new AssertionError(message);
}
// Verify the response body
int mismatchIndex = Arrays.mismatch(expectedBytes, response.body());
if (mismatchIndex > 0) {
var message = String.format(
"Body mismatch at index %s (attempt=%s)",
mismatchIndex, requestIndex + 1);
throw new AssertionError(message);
}
}
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2025, 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 8368528
* @summary Verifies that `Deadline` returns extremums on numeric overflows
* @modules java.net.http/jdk.internal.net.http.common:+open
* @run junit java.net.http/jdk.internal.net.http.common.DeadlineOverflowTest
*/

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2025, 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.
*/
package jdk.internal.net.http.common;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static java.time.temporal.ChronoUnit.NANOS;
import static org.junit.jupiter.api.Assertions.assertEquals;
class DeadlineOverflowTest {
@Test
void test_DeadlineOf_InstantMin() {
assertEquals(Instant.MIN, Deadline.of(Instant.MIN).asInstant());
}
@Test
void test_DeadlineOf_InstantMax() {
assertEquals(Instant.MAX, Deadline.of(Instant.MAX).asInstant());
}
@Test
void test_plusNanos_min() {
assertEquals(Deadline.MIN, Deadline.MIN.plusNanos(-1));
}
@Test
void test_plusNanos_max() {
assertEquals(Deadline.MAX, Deadline.MAX.plusNanos(1));
}
@Test
void test_minus_min() {
assertEquals(Deadline.MIN, Deadline.MIN.minus(Duration.ofNanos(1)));
}
@Test
void test_minus_max() {
assertEquals(Deadline.MAX, Deadline.MAX.minus(Duration.ofNanos(-1)));
}
@Test
void test_plusAmount_min() {
assertEquals(Deadline.MIN, Deadline.MIN.plus(-1, ChronoUnit.NANOS));
}
@Test
void test_plusAmount_max() {
assertEquals(Deadline.MAX, Deadline.MAX.plus(1, ChronoUnit.NANOS));
}
@Test
void test_plusSeconds_min() {
assertEquals(Deadline.MIN, Deadline.MIN.plusSeconds(-1));
}
@Test
void test_plusSeconds_max() {
assertEquals(Deadline.MAX, Deadline.MAX.plusSeconds(1));
}
@Test
void test_plusMillis_min() {
assertEquals(Deadline.MIN, Deadline.MIN.plusMillis(-1));
}
@Test
void test_plusMillis_max() {
assertEquals(Deadline.MAX, Deadline.MAX.plusMillis(1));
}
@Test
void test_plusDuration_min() {
assertEquals(Deadline.MIN, Deadline.MIN.plus(Duration.ofNanos(-1)));
}
@Test
void test_plusDuration_max() {
assertEquals(Deadline.MAX, Deadline.MAX.plus(Duration.ofNanos(1)));
}
@Test
void test_until_min() {
assertEquals(Long.MIN_VALUE, Deadline.MAX.until(Deadline.MIN, NANOS));
}
@Test
void test_until_max() {
assertEquals(Long.MAX_VALUE, Deadline.MIN.until(Deadline.MAX, NANOS));
}
@Test
void test_between_min() {
Duration delta = Duration.between(Instant.MAX, Instant.MIN);
assertEquals(delta, Deadline.between(Deadline.MAX, Deadline.MIN));
}
@Test
void test_between_max() {
Duration delta = Duration.between(Instant.MIN, Instant.MAX);
assertEquals(delta, Deadline.between(Deadline.MIN, Deadline.MAX));
}
}