diff --git a/README.md b/README.md index 3e8a80630..0ecac8666 100644 --- a/README.md +++ b/README.md @@ -614,6 +614,24 @@ Requests can be executed asynchronously, using the `executeAsync()` method, whic ## API Clients Recommendations The SDK implements a custom networking stack on top of the **OkHttp** library. The [official recommendation](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/#okhttpclients-should-be-shared) from Square is to re-use as much as possible these clients. However, it's not possible to pass an existing `OkHttpClient` instance to our `AuthAPI` and `ManagementAPI` clients. +The networking client used by both the `AuthAPI` and `ManagementAPI` clients can be configured through the `HttpOptions`, which enables custom timeout configuration and proxy support: + +```java +HttpOptions options = new HttpOptions(); + +// configure timeouts; default is ten seconds for both connect and read timeouts: +options.setConnectTimeout(5); +options.setReadTimeout(15); + +// configure proxy: +Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("{IP-ADDRESS}", {PORT})); +ProxyOptions proxyOptions = new ProxyOptions(proxy); +options.setProxyOptions(proxyOptions); + +// create client +AuthAPI authAPI = new AuthAPI("{CLIENT_ID}", "{CLIENT_SECRET}", options); +``` + Whenever you instantiate a client, a new `OkHttpClient` instance is created internally to handle the network requests. This instance is not directly exposed for customization. In order to reduce resource consumption, make use of the _singleton pattern_ to keep a single instance of this SDK's API client during the lifecycle of your application. For the particular case of the `ManagementAPI` client, if the token you've originally set has expired or you require to change its scopes, you can update the client's token with the `setApiToken(String)` method. diff --git a/src/main/java/com/auth0/client/HttpOptions.java b/src/main/java/com/auth0/client/HttpOptions.java index f0e8c721c..0a4823580 100644 --- a/src/main/java/com/auth0/client/HttpOptions.java +++ b/src/main/java/com/auth0/client/HttpOptions.java @@ -6,6 +6,8 @@ public class HttpOptions { private ProxyOptions proxyOptions; + private int connectTimeout = 10; + private int readTimeout = 10; /** * Getter for the Proxy configuration options @@ -24,4 +26,44 @@ public ProxyOptions getProxyOptions() { public void setProxyOptions(ProxyOptions proxyOptions) { this.proxyOptions = proxyOptions; } + + + /** + * @return the connect timeout, in seconds + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Sets the value of the connect timeout, in seconds. Defaults to ten seconds. A value of zero results in no connect timeout. + * Negative numbers will be treated as zero. + * @param connectTimeout the value of the connect timeout to use. + */ + public void setConnectTimeout(int connectTimeout) { + if (connectTimeout < 0) { + connectTimeout = 0; + } + this.connectTimeout = connectTimeout; + } + + /** + * @return the read timeout, in seconds + */ + public int getReadTimeout() { + return readTimeout; + } + + /** + * Sets the value of the read timeout, in seconds. Defaults to ten seconds. A value of zero results in no read timeout. + * Negative numbers will be treated as zero. + * + * @param readTimeout the value of the read timeout to use. + */ + public void setReadTimeout(int readTimeout) { + if (readTimeout < 0) { + readTimeout = 0; + } + this.readTimeout = readTimeout; + } } diff --git a/src/main/java/com/auth0/client/auth/AuthAPI.java b/src/main/java/com/auth0/client/auth/AuthAPI.java index 26d693b69..f84b47424 100644 --- a/src/main/java/com/auth0/client/auth/AuthAPI.java +++ b/src/main/java/com/auth0/client/auth/AuthAPI.java @@ -14,6 +14,7 @@ import okhttp3.logging.HttpLoggingInterceptor.Level; import java.io.IOException; +import java.util.concurrent.TimeUnit; /** * Class that provides an implementation of some of the Authentication and Authorization API methods defined in https://auth0.com/docs/api/authentication. @@ -131,6 +132,8 @@ public okhttp3.Request authenticate(Route route, Response response) throws IOExc return clientBuilder .addInterceptor(logging) .addInterceptor(telemetry) + .connectTimeout(options.getConnectTimeout(), TimeUnit.SECONDS) + .readTimeout(options.getReadTimeout(), TimeUnit.SECONDS) .build(); } diff --git a/src/main/java/com/auth0/client/mgmt/ManagementAPI.java b/src/main/java/com/auth0/client/mgmt/ManagementAPI.java index 17efb8406..566f8b0d6 100644 --- a/src/main/java/com/auth0/client/mgmt/ManagementAPI.java +++ b/src/main/java/com/auth0/client/mgmt/ManagementAPI.java @@ -10,6 +10,7 @@ import okhttp3.logging.HttpLoggingInterceptor.Level; import java.io.IOException; +import java.util.concurrent.TimeUnit; /** * Class that provides an implementation of the Management API methods defined in https://auth0.com/docs/api/management/v2. @@ -102,6 +103,8 @@ public okhttp3.Request authenticate(Route route, Response response) throws IOExc return clientBuilder .addInterceptor(logging) .addInterceptor(telemetry) + .connectTimeout(options.getConnectTimeout(), TimeUnit.SECONDS) + .readTimeout(options.getReadTimeout(), TimeUnit.SECONDS) .build(); } diff --git a/src/test/java/com/auth0/client/auth/AuthAPITest.java b/src/test/java/com/auth0/client/auth/AuthAPITest.java index 0b3e09511..8f3c82838 100644 --- a/src/test/java/com/auth0/client/auth/AuthAPITest.java +++ b/src/test/java/com/auth0/client/auth/AuthAPITest.java @@ -105,6 +105,35 @@ public void shouldThrowWhenClientSecretIsNull() { new AuthAPI(DOMAIN, CLIENT_ID, null); } + @Test + public void shouldUseDefaultTimeValues() { + AuthAPI api = new AuthAPI(DOMAIN, CLIENT_ID, CLIENT_SECRET); + assertThat(api.getClient().connectTimeoutMillis(), is(10 * 1000)); + assertThat(api.getClient().readTimeoutMillis(), is(10 * 1000)); + } + + @Test + public void shouldUseConfiguredTimeoutValues() { + HttpOptions options = new HttpOptions(); + options.setConnectTimeout(20); + options.setReadTimeout(30); + AuthAPI api = new AuthAPI(DOMAIN, CLIENT_ID, CLIENT_SECRET, options); + + assertThat(api.getClient().connectTimeoutMillis(), is(20 * 1000)); + assertThat(api.getClient().readTimeoutMillis(), is(30 * 1000)); + } + + @Test + public void shouldUseZeroIfNegativeTimoutConfigured() { + HttpOptions options = new HttpOptions(); + options.setConnectTimeout(-1); + options.setReadTimeout(-10); + AuthAPI api = new AuthAPI(DOMAIN, CLIENT_ID, CLIENT_SECRET, options); + + assertThat(api.getClient().connectTimeoutMillis(), is(0)); + assertThat(api.getClient().readTimeoutMillis(), is(0)); + } + @Test public void shouldNotUseProxyByDefault() throws Exception { AuthAPI api = new AuthAPI(DOMAIN, CLIENT_ID, CLIENT_SECRET); diff --git a/src/test/java/com/auth0/client/mgmt/ManagementAPITest.java b/src/test/java/com/auth0/client/mgmt/ManagementAPITest.java index 7f6e9d52f..e5bc60149 100644 --- a/src/test/java/com/auth0/client/mgmt/ManagementAPITest.java +++ b/src/test/java/com/auth0/client/mgmt/ManagementAPITest.java @@ -139,6 +139,34 @@ public void shouldUpdateApiToken() { assertThat(api.users().apiToken, is("new token")); } + @Test + public void shouldUseDefaultTimeoutIfNotSpecified() { + ManagementAPI api = new ManagementAPI(DOMAIN, API_TOKEN); + assertThat(api.getClient().connectTimeoutMillis(), is(10 * 1000)); + assertThat(api.getClient().readTimeoutMillis(), is(10 * 1000)); + } + + @Test + public void shouldUseZeroIfNegativeTimeoutConfigured() { + HttpOptions options = new HttpOptions(); + options.setConnectTimeout(-1); + options.setReadTimeout(-1); + ManagementAPI api = new ManagementAPI(DOMAIN, API_TOKEN, options); + assertThat(api.getClient().connectTimeoutMillis(), is(0)); + assertThat(api.getClient().readTimeoutMillis(), is(0)); + + } + + @Test + public void shouldSetTimeoutsIfConfigured() { + HttpOptions options = new HttpOptions(); + options.setConnectTimeout(20); + options.setReadTimeout(30); + ManagementAPI api = new ManagementAPI(DOMAIN, API_TOKEN, options); + assertThat(api.getClient().connectTimeoutMillis(), is(20 * 1000)); + assertThat(api.getClient().readTimeoutMillis(), is(30 * 1000)); + } + @Test public void shouldNotUseProxyByDefault() throws Exception { ManagementAPI api = new ManagementAPI(DOMAIN, API_TOKEN);