diff --git a/.codegen.json b/.codegen.json index bff58a927..31f95bead 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "dcdff3f", "specHash": "a05e5d7", "version": "0.1.0" } +{ "engineHash": "ee965c4", "specHash": "a05e5d7", "version": "0.1.0" } diff --git a/docs/Client.md b/docs/Client.md index f1cd01706..9a871d55e 100644 --- a/docs/Client.md +++ b/docs/Client.md @@ -17,6 +17,7 @@ divided across resource managers. - [Custom headers](#custom-headers) - [Custom Base URLs](#custom-base-urls) - [Interceptors](#interceptors) +- [Use Proxy for API calls](#use-proxy-for-api-calls) @@ -176,3 +177,18 @@ List interceptors = new ArrayList<>() { }; BoxClient clientWithInterceptor = client.withInterceptors(interceptors); ``` + +# Use Proxy for API calls + +In order to use a proxy for API calls, calling the `client.withProxy(proxyConfig)` method creates a new client, leaving the original client unmodified, with the username and password being optional. We only support adding proxy for BoxNetworkClient. If you are using your own implementation of NetworkClient, you would need to configure proxy on your own. + +**Note:** We are only supporting http/s proxies with basic authentication. NTLM and other authentication methods are not supported. + +```java +ProxyConfig proxyConfig = new ProxyConfig("http://127.0.0.1:3128"); +newClient = client.withProxy(proxyConfig); + +//Using Basic Auth with username and password +ProxyConfig proxyConfig = new ProxyConfig.Builder("http://127.0.0.1:3128").username("username").password("password").build(); +newClient = client.withProxy(proxyConfig); +``` diff --git a/src/main/java/com/box/sdkgen/networking/boxnetworkclient/BoxNetworkClient.java b/src/main/java/com/box/sdkgen/networking/boxnetworkclient/BoxNetworkClient.java index 2fd150064..261b24b4d 100644 --- a/src/main/java/com/box/sdkgen/networking/boxnetworkclient/BoxNetworkClient.java +++ b/src/main/java/com/box/sdkgen/networking/boxnetworkclient/BoxNetworkClient.java @@ -18,16 +18,22 @@ import com.box.sdkgen.networking.fetchresponse.FetchResponse; import com.box.sdkgen.networking.network.NetworkSession; import com.box.sdkgen.networking.networkclient.NetworkClient; +import com.box.sdkgen.networking.proxyconfig.ProxyConfig; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import okhttp3.Call; +import okhttp3.Credentials; import okhttp3.Headers; import okhttp3.HttpUrl; import okhttp3.MediaType; @@ -44,6 +50,8 @@ public class BoxNetworkClient implements NetworkClient { private static final int BASE_TIMEOUT = 1; private static final double RANDOM_FACTOR = 0.5; + private static final int DEFAULT_HTTP_PORT = 80; + private static final int DEFAULT_HTTPS_PORT = 443; protected OkHttpClient httpClient; @@ -64,6 +72,36 @@ public OkHttpClient getHttpClient() { return httpClient; } + public BoxNetworkClient withProxy(ProxyConfig config) { + URI uri = URI.create(config.getUrl()); + String host = Objects.requireNonNull(uri.getHost(), "Invalid Proxy URL"); + + String scheme = + Optional.ofNullable(uri.getScheme()) + .filter(schema -> schema.startsWith("http")) + .orElseThrow(() -> new IllegalArgumentException("Invalid Proxy URL: " + uri)); + + int port = + (uri.getPort() != -1) + ? uri.getPort() + : ("https".equalsIgnoreCase(scheme) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT); + + OkHttpClient.Builder clientBuilder = + httpClient + .newBuilder() + .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port))); + + String username = config.getUsername(); + String password = config.getPassword(); + if (username != null && !username.trim().isEmpty() && password != null) { + String basic = Credentials.basic(username, password, StandardCharsets.UTF_8); + clientBuilder.proxyAuthenticator( + (route, resp) -> + resp.request().newBuilder().header("Proxy-Authorization", basic).build()); + } + return new BoxNetworkClient(clientBuilder.build()); + } + public FetchResponse fetch(FetchOptions options) { NetworkSession networkSession = options.getNetworkSession() == null ? new NetworkSession() : options.getNetworkSession(); diff --git a/src/main/java/com/box/sdkgen/networking/network/NetworkSession.java b/src/main/java/com/box/sdkgen/networking/network/NetworkSession.java index 4206ff5a6..3c453ec9d 100644 --- a/src/main/java/com/box/sdkgen/networking/network/NetworkSession.java +++ b/src/main/java/com/box/sdkgen/networking/network/NetworkSession.java @@ -1,10 +1,12 @@ package com.box.sdkgen.networking.network; +import com.box.sdkgen.box.errors.BoxSDKError; import com.box.sdkgen.internal.logging.DataSanitizer; import com.box.sdkgen.networking.baseurls.BaseUrls; import com.box.sdkgen.networking.boxnetworkclient.BoxNetworkClient; import com.box.sdkgen.networking.interceptors.Interceptor; import com.box.sdkgen.networking.networkclient.NetworkClient; +import com.box.sdkgen.networking.proxyconfig.ProxyConfig; import com.box.sdkgen.networking.retries.BoxRetryStrategy; import com.box.sdkgen.networking.retries.RetryStrategy; import java.util.ArrayList; @@ -27,6 +29,8 @@ public class NetworkSession { protected DataSanitizer dataSanitizer; + protected ProxyConfig proxyConfig; + public NetworkSession() { networkClient = new BoxNetworkClient(); retryStrategy = new BoxRetryStrategy(); @@ -40,6 +44,7 @@ protected NetworkSession(Builder builder) { this.interceptors = builder.interceptors; this.retryStrategy = builder.retryStrategy; this.dataSanitizer = builder.dataSanitizer; + this.proxyConfig = builder.proxyConfig; } public NetworkSession withAdditionalHeaders() { @@ -56,7 +61,8 @@ public NetworkSession withAdditionalHeaders(Map additionalHeader .interceptors(this.interceptors) .networkClient(this.networkClient) .retryStrategy(this.retryStrategy) - .dataSanitizer(dataSanitizer) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(this.proxyConfig) .build(); } @@ -67,7 +73,8 @@ public NetworkSession withCustomBaseUrls(BaseUrls baseUrls) { .interceptors(this.interceptors) .networkClient(this.networkClient) .retryStrategy(this.retryStrategy) - .dataSanitizer(dataSanitizer) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(this.proxyConfig) .build(); } @@ -81,7 +88,8 @@ public NetworkSession withInterceptors(List interceptors) { .interceptors(newInterceptors) .networkClient(this.networkClient) .retryStrategy(this.retryStrategy) - .dataSanitizer(dataSanitizer) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(this.proxyConfig) .build(); } @@ -92,7 +100,8 @@ public NetworkSession withNetworkClient(NetworkClient networkClient) { .interceptors(this.interceptors) .networkClient(networkClient) .retryStrategy(this.retryStrategy) - .dataSanitizer(dataSanitizer) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(this.proxyConfig) .build(); } @@ -103,7 +112,8 @@ public NetworkSession withRetryStrategy(RetryStrategy retryStrategy) { .interceptors(this.interceptors) .networkClient(this.networkClient) .retryStrategy(retryStrategy) - .dataSanitizer(dataSanitizer) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(this.proxyConfig) .build(); } @@ -115,6 +125,26 @@ public NetworkSession withDataSanitizer(DataSanitizer dataSanitizer) { .networkClient(this.networkClient) .retryStrategy(this.retryStrategy) .dataSanitizer(dataSanitizer) + .proxyConfig(this.proxyConfig) + .build(); + } + + public NetworkSession withProxy(ProxyConfig config) { + if (config == null) { + throw new IllegalArgumentException("ProxyConfig cannot be null"); + } + if (!(this.networkClient instanceof BoxNetworkClient)) { + throw new BoxSDKError("Proxies are only supported for BoxNetworkClient"); + } + BoxNetworkClient newClient = ((BoxNetworkClient) this.networkClient).withProxy(config); + return new Builder() + .additionalHeaders(this.additionalHeaders) + .baseUrls(this.baseUrls) + .interceptors(this.interceptors) + .networkClient(newClient) + .retryStrategy(this.retryStrategy) + .dataSanitizer(this.dataSanitizer) + .proxyConfig(config) .build(); } @@ -142,6 +172,10 @@ public DataSanitizer getDataSanitizer() { return dataSanitizer; } + public ProxyConfig getProxyConfig() { + return proxyConfig; + } + public static class Builder { protected Map additionalHeaders = new HashMap<>(); @@ -156,6 +190,8 @@ public static class Builder { protected DataSanitizer dataSanitizer; + protected ProxyConfig proxyConfig; + public Builder() { networkClient = new BoxNetworkClient(); retryStrategy = new BoxRetryStrategy(); @@ -192,6 +228,11 @@ public Builder dataSanitizer(DataSanitizer dataSanitizer) { return this; } + public Builder proxyConfig(ProxyConfig proxyConfig) { + this.proxyConfig = proxyConfig; + return this; + } + public NetworkSession build() { return new NetworkSession(this); } diff --git a/src/main/java/com/box/sdkgen/networking/proxyconfig/ProxyConfig.java b/src/main/java/com/box/sdkgen/networking/proxyconfig/ProxyConfig.java new file mode 100644 index 000000000..83ff1b7fb --- /dev/null +++ b/src/main/java/com/box/sdkgen/networking/proxyconfig/ProxyConfig.java @@ -0,0 +1,59 @@ +package com.box.sdkgen.networking.proxyconfig; + +public class ProxyConfig { + + public final String url; + + public String username; + + public String password; + + public ProxyConfig(String url) { + this.url = url; + } + + protected ProxyConfig(Builder builder) { + this.url = builder.url; + this.username = builder.username; + this.password = builder.password; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public static class Builder { + + protected final String url; + + protected String username; + + protected String password; + + public Builder(String url) { + this.url = url; + } + + public Builder username(String username) { + this.username = username; + return this; + } + + public Builder password(String password) { + this.password = password; + return this; + } + + public ProxyConfig build() { + return new ProxyConfig(this); + } + } +}