diff --git a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/JsonRpcClient.java b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/JsonRpcClient.java index 75a25ddd5..0c8a3ad4e 100644 --- a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/JsonRpcClient.java +++ b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/JsonRpcClient.java @@ -27,6 +27,7 @@ import com.google.common.annotations.Beta; import feign.Feign; import feign.Headers; +import feign.Request.Options; import feign.RequestLine; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; @@ -71,18 +72,34 @@ public interface JsonRpcClient { /** * Constructs a new client for the given url. * - * @param rippledUrl url for the faucet server. + * @param rippledUrl The {@link HttpUrl} of the node to connect to. * * @return A {@link JsonRpcClient} that can make request to {@code rippledUrl} */ static JsonRpcClient construct(final HttpUrl rippledUrl) { Objects.requireNonNull(rippledUrl); + return construct(rippledUrl, new Options()); + } + + /** + * Constructs a new client for the given url with the given client options. + * + * @param rippledUrl The {@link HttpUrl} of the node to connect to. + * @param options An {@link Options}. + * + * @return A {@link JsonRpcClient}. + */ + static JsonRpcClient construct(HttpUrl rippledUrl, Options options) { + Objects.requireNonNull(rippledUrl); + Objects.requireNonNull(options); + return Feign.builder() .encoder(new JacksonEncoder(objectMapper)) // rate limiting will return a 503 status that can be retried .errorDecoder(new RetryStatusDecoder(RETRY_INTERVAL, SERVICE_UNAVAILABLE_STATUS)) .decode404() + .options(options) .decoder(new OptionalDecoder(new JacksonDecoder(objectMapper))) .target(JsonRpcClient.class, rippledUrl.toString()); } diff --git a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java index bf744ea25..26ef8179a 100644 --- a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java +++ b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java @@ -28,6 +28,8 @@ import com.google.common.collect.Range; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; +import feign.Request; +import feign.Request.Options; import okhttp3.HttpUrl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,8 +101,10 @@ import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.TransactionMetadata; +import java.time.Duration; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.TimeUnit; /** *

A client which wraps a rippled network client and is responsible for higher order functionality such as signing @@ -127,6 +131,31 @@ public XrplClient(final HttpUrl rippledUrl) { this(JsonRpcClient.construct(rippledUrl)); } + /** + * Public constructor that allows for configuration of connect and read timeouts. + * + *

Note that any {@link Duration} passed in that is less than one millisecond will result in the actual timeout + * being zero milliseconds. It is therefore advised to never set {@code connectTimeout} or {@code readTimeout} to a + * {@link Duration} less than one millisecond. + * + * @param rippledUrl The {@link HttpUrl} of the node to connect to. + * @param connectTimeout A {@link Duration} indicating the client's connect timeout. + * @param readTimeout A {@link Duration} indicating the client's read timeout. + */ + public XrplClient( + HttpUrl rippledUrl, + Duration connectTimeout, + Duration readTimeout + ) { + this( + JsonRpcClient.construct( + rippledUrl, + new Options(connectTimeout.toMillis(), TimeUnit.MILLISECONDS, readTimeout.toMillis(), TimeUnit.MILLISECONDS, + true) + ) + ); + } + /** * Required-args constructor (exists for testing purposes only). * diff --git a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java index 32c4e05be..2aa720346 100644 --- a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java +++ b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java @@ -122,9 +122,11 @@ import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import java.math.BigDecimal; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** @@ -599,6 +601,18 @@ public void getXrplClientTest() { assertThat(new XrplClient(rippledUrl) instanceof JsonRpcClient).isFalse(); } + @Test + void createXrplClientWithDurationTimeouts() { + HttpUrl rippledUrl = HttpUrl.parse("https://s.altnet.rippletest.net:51234"); + XrplClient client = new XrplClient( + rippledUrl, + Duration.ofSeconds(1), + Duration.ofMinutes(2) + ); + + assertThat(client).isInstanceOf(XrplClient.class); + } + @Test public void submitSingleSignedTransaction() { BcSignatureService bcSignatureService = new BcSignatureService();