Skip to content

Commit

Permalink
HttpClientImpl modifications
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea committed Dec 7, 2023
1 parent 5461f25 commit e4dff34
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 158 deletions.
226 changes: 117 additions & 109 deletions src/main/java/io/split/android/client/network/HttpClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,53 @@

import com.google.common.base.Strings;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLSocketFactory;

import io.split.android.client.utils.Base64Util;
import io.split.android.client.utils.logger.Logger;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

public class HttpClientImpl implements HttpClient {
private static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
private static final long STREAMING_READ_TIMEOUT_IN_MILLISECONDS = 80000;

private final OkHttpClient mOkHttpClient;
private final OkHttpClient mOkHttpClientStreaming;
private final Map<String, String> mCommonHeaders;
private final Map<String, String> mStreamingHeaders;

private HttpClientImpl(OkHttpClient okHttpClient, OkHttpClient okHttpClientStreaming) {
@Nullable
private final Proxy mProxy;
@Nullable
private final SplitUrlConnectionAuthenticator mProxyAuthenticator;
private final long mReadTimeout;
private final long mConnectionTimeout;
@Nullable
private final DevelopmentSslConfig mDevelopmentSslConfig;
@Nullable
private final SSLSocketFactory mSslSocketFactory;
@NonNull
private final UrlSanitizer mUrlSanitizer;

HttpClientImpl(@Nullable HttpProxy proxy,
@Nullable SplitAuthenticator proxyAuthenticator,
long readTimeout,
long connectionTimeout,
@Nullable DevelopmentSslConfig developmentSslConfig,
@Nullable SSLSocketFactory sslSocketFactory,
@NonNull UrlSanitizer urlSanitizer) {
mProxy = initializeProxy(proxy);
mProxyAuthenticator = initializeProxyAuthenticator(proxy, proxyAuthenticator);
mReadTimeout = readTimeout;
mConnectionTimeout = connectionTimeout;
mDevelopmentSslConfig = developmentSslConfig;
mCommonHeaders = new HashMap<>();
mStreamingHeaders = new HashMap<>();
mOkHttpClient = okHttpClient;
mOkHttpClientStreaming = okHttpClientStreaming;
mSslSocketFactory = sslSocketFactory;
mUrlSanitizer = urlSanitizer;
}

@Override
Expand All @@ -48,7 +63,18 @@ public HttpRequest request(URI uri, HttpMethod requestMethod, String body, Map<S
newHeaders.putAll(headers);
}

return new HttpRequestImpl(mOkHttpClient, uri, requestMethod, body, newHeaders);
return new HttpRequestImpl(
uri,
requestMethod,
body,
newHeaders,
mProxy,
mProxyAuthenticator,
mReadTimeout,
mConnectionTimeout,
mDevelopmentSslConfig,
mSslSocketFactory,
mUrlSanitizer);
}

public HttpRequest request(URI uri, HttpMethod requestMethod) {
Expand All @@ -62,7 +88,14 @@ public HttpRequest request(URI uri, HttpMethod requestMethod, String body) {

@Override
public HttpStreamRequest streamRequest(URI uri) {
return new HttpStreamRequestImpl(mOkHttpClientStreaming, uri, mStreamingHeaders);
return new HttpStreamRequestImpl(uri,
mStreamingHeaders,
mProxy,
mProxyAuthenticator,
mConnectionTimeout,
mDevelopmentSslConfig,
mSslSocketFactory,
mUrlSanitizer);
}

@Override
Expand Down Expand Up @@ -97,17 +130,49 @@ public void addStreamingHeaders(Map<String, String> headers) {

@Override
public void close() {
mOkHttpClient.connectionPool().evictAll();
mOkHttpClientStreaming.connectionPool().evictAll();

}

private Proxy initializeProxy(HttpProxy proxy) {
if (proxy != null) {
return new Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(proxy.getHost(), proxy.getPort()));
}

return null;
}

private SplitUrlConnectionAuthenticator initializeProxyAuthenticator(HttpProxy proxy, SplitAuthenticator proxyAuthenticator) {
if (proxy == null) {
return null;
} else if (proxyAuthenticator != null) {
return new SplitUrlConnectionAuthenticator(proxyAuthenticator);
} else if (!Strings.isNullOrEmpty(proxy.getUsername())) {
return createBasicAuthenticator(proxy.getUsername(), proxy.getPassword());
}

return null;
}

private static SplitUrlConnectionAuthenticator createBasicAuthenticator(String username, String password) {
return new SplitUrlConnectionAuthenticator(new SplitBasicAuthenticator(username, password, new SplitBasicAuthenticator.Base64Encoder() {
@Override
public String encode(String value) {
return Base64Util.encode(value);
}
}));
}

public static class Builder {
private SplitAuthenticator mProxyAuthenticator;
private HttpProxy mProxy;
private long readTimeout = -1;
private long connectionTimeout = -1;
private DevelopmentSslConfig developmentSslConfig = null;
private long mReadTimeout = -1;
private long mConnectionTimeout = -1;
private DevelopmentSslConfig mDevelopmentSslConfig = null;
private SSLSocketFactory mSslSocketFactory = null;
private Context mHostAppContext;
private UrlSanitizer mUrlSanitizer;

public Builder setContext(Context context) {
mHostAppContext = context;
Expand All @@ -120,114 +185,57 @@ public Builder setProxy(HttpProxy proxy) {
}

public Builder setProxyAuthenticator(SplitAuthenticator authenticator) {
Logger.v("Setting up proxy authenticator");
mProxyAuthenticator = authenticator;
return this;
}

public Builder setReadTimeout(long readTimeout) {
this.readTimeout = readTimeout;
if (readTimeout > 0) {
mReadTimeout = readTimeout;
}
return this;
}

public Builder setConnectionTimeout(long connectionTimeout) {
this.connectionTimeout = connectionTimeout;
if (connectionTimeout > 0) {
mConnectionTimeout = connectionTimeout;
}
return this;
}

public Builder setDevelopmentSslConfig(DevelopmentSslConfig developmentSslConfig) {
this.developmentSslConfig = developmentSslConfig;
mDevelopmentSslConfig = developmentSslConfig;
return this;
}

public Builder setUrlSanitizer(UrlSanitizer urlSanitizer) {
mUrlSanitizer = urlSanitizer;
return this;
}

public HttpClient build() {
Proxy proxy = null;
okhttp3.Authenticator proxyAuthenticator = null;
if (mProxy != null) {
proxy = createProxy(mProxy);
if (mProxyAuthenticator != null) {
proxyAuthenticator = createAuthenticator(mProxyAuthenticator);
} else if (!Strings.isNullOrEmpty(mProxy.getUsername())) {
proxyAuthenticator = createBasicAuthenticator(mProxy.getUsername(), mProxy.getPassword());
if (mDevelopmentSslConfig == null) {
if (LegacyTlsUpdater.couldBeOld()) {
LegacyTlsUpdater.update(mHostAppContext);
try {
mSslSocketFactory = new Tls12OnlySocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Logger.e("TLS v12 algorithm not available: " + e.getLocalizedMessage());
} catch (Exception e) {
Logger.e("Unknown TLS v12 error: " + e.getLocalizedMessage());
}
}
}

// Avoiding newBuilder on purpose to use different thread pool and resources
return new HttpClientImpl(
createOkHttpClient(proxy, proxyAuthenticator, readTimeout, connectionTimeout, developmentSslConfig, mHostAppContext),
createOkHttpClient(proxy, proxyAuthenticator, STREAMING_READ_TIMEOUT_IN_MILLISECONDS,
connectionTimeout, developmentSslConfig, mHostAppContext)
);
}

private OkHttpClient createOkHttpClient(Proxy proxy,
okhttp3.Authenticator proxyAuthenticator,
Long readTimeout,
Long connectionTimeout,
DevelopmentSslConfig developmentSslConfig,
Context context) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (proxy != null) {
builder.proxy(proxy);
}

if (proxyAuthenticator != null) {
Logger.v("Setting up proxy authenticator");
builder.proxyAuthenticator(proxyAuthenticator);
}

if (readTimeout != null && readTimeout > 0) {
builder.readTimeout(readTimeout, TimeUnit.MILLISECONDS);
}

if (connectionTimeout != null && connectionTimeout > 0) {
builder.connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS);
}

// Both options overrides SSLSocketFactory
if (developmentSslConfig != null) {
builder.sslSocketFactory(developmentSslConfig.getSslSocketFactory(), developmentSslConfig.getTrustManager());
builder.hostnameVerifier(developmentSslConfig.getHostnameVerifier());
} else if (LegacyTlsUpdater.couldBeOld()) {
forceTls12OnOldAndroid(builder, context);
}
return builder.build();
}

private Proxy createProxy(HttpProxy proxy) {
if (proxy == null) {
return null;
}
return new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(proxy.getHost(), proxy.getPort()));
}

private okhttp3.Authenticator createBasicAuthenticator(String username, String password) {
return new okhttp3.Authenticator() {
@Nullable
@Override
public Request authenticate(@Nullable Route route, @NonNull Response response) throws IOException {
String credential = Credentials.basic(username, password);
return response.request().newBuilder().header(PROXY_AUTHORIZATION_HEADER, credential).build();
}
};
}

private okhttp3.Authenticator createAuthenticator(SplitAuthenticator authenticator) {
return new SplitOkHttpAuthenticator(authenticator);
}

private void forceTls12OnOldAndroid(OkHttpClient.Builder okHttpBuilder, Context context) {

LegacyTlsUpdater.update(context);
try {
Tls12OnlySocketFactory factory = new Tls12OnlySocketFactory();
okHttpBuilder.sslSocketFactory(factory, factory.defaultTrustManager());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Logger.e("TLS v12 algorithm not available: " + e.getLocalizedMessage());
} catch (GeneralSecurityException e) {
Logger.e("TLS v12 security error: " + e.getLocalizedMessage());
} catch (Exception e) {
Logger.e("Unknown TLS v12 error: " + e.getLocalizedMessage());
}
mProxy,
mProxyAuthenticator,
mReadTimeout,
mConnectionTimeout,
mDevelopmentSslConfig,
mSslSocketFactory,
(mUrlSanitizer == null) ? new UrlSanitizerImpl() : mUrlSanitizer);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.split.android.client.network;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

class SplitBasicAuthenticator extends SplitAuthenticator {

private static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
private final String mUsername;
private final String mPassword;
@NonNull
private final Base64Encoder mBase64Encoder;

SplitBasicAuthenticator(String username, String password, @NonNull Base64Encoder base64Encoder) {
mUsername = username;
mPassword = password;
mBase64Encoder = base64Encoder;
}

@Nullable
@Override
public SplitAuthenticatedRequest authenticate(@NonNull SplitAuthenticatedRequest request) {
String credential = basic(mUsername, mPassword);
request.setHeader(PROXY_AUTHORIZATION_HEADER, credential);

return request;
}

private String basic(String username, String password) {
String usernameAndPassword = username + ":" + password;
String encoded = mBase64Encoder.encode(usernameAndPassword);
return "Basic " + encoded;
}

interface Base64Encoder {
String encode(String value);
}
}

This file was deleted.

Loading

0 comments on commit e4dff34

Please sign in to comment.