Skip to content

Commit

Permalink
Inject configured OkHttpClient to networking module using MainPackage…
Browse files Browse the repository at this point in the history
…Config

- Most brownfield application will have their OkHttpClient configured with custom headers, interceptors, logging, etc. This will enable them to inject the same http client to react-native networking subsystem. This also allows them to use fetch in javascript.
  • Loading branch information
thotegowda committed May 31, 2017
1 parent 1dd7bc1 commit 71217d1
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

package com.facebook.react.modules.fresco;

import java.util.HashSet;

import android.content.Context;
import android.support.annotation.Nullable;

Expand All @@ -28,10 +26,12 @@
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.common.ModuleDataCleaner;
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.DefaultOkHttpProvider;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.soloader.SoLoader;

import java.util.HashSet;

import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;

Expand Down Expand Up @@ -154,7 +154,7 @@ public static ImagePipelineConfig.Builder getDefaultConfigBuilder(ReactContext c
HashSet<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new SystraceRequestListener());

OkHttpClient client = OkHttpClientProvider.createClient();
OkHttpClient client = new DefaultOkHttpProvider().get();

// make sure to forward cookies for any requests via the okHttpClient
// so that image requests to endpoints that use cookies still work
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

package com.facebook.react.modules.network;

import android.os.Build;

import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;

public class DefaultOkHttpProvider implements OkHttpClientProvider {

public OkHttpClient get() {
// No timeouts by default
OkHttpClient.Builder client = new OkHttpClient.Builder()
.connectTimeout(0, TimeUnit.MILLISECONDS)
.readTimeout(0, TimeUnit.MILLISECONDS)
.writeTimeout(0, TimeUnit.MILLISECONDS)
.cookieJar(new ReactCookieJarContainer());

return enableTls12OnPreLollipop(client).build();
}

/**
* On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are
* available but not enabled by default. The following method
* enables it.
*/
private static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
client.sslSocketFactory(new TLSSocketFactory());

ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();

List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);

client.connectionSpecs(specs);
} catch (Exception e) {
FLog.e(ReactConstants.TAG, "Error while enabling TLS 1.2", e);
}
}
return client;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@

package com.facebook.react.modules.network;

import javax.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import android.util.Base64;

import com.facebook.react.bridge.Arguments;
Expand All @@ -33,6 +23,16 @@
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.CookieJar;
Expand Down Expand Up @@ -76,43 +76,58 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
/* package */ NetworkingModule(
ReactApplicationContext reactContext,
@Nullable String defaultUserAgent,
OkHttpClient client,
OkHttpClientProvider httpClientProvider,
@Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
super(reactContext);

mClient = createOkHttpClient(httpClientProvider, networkInterceptorCreators);
mCookieHandler = new ForwardingCookieHandler(reactContext);
mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
mShuttingDown = false;
mDefaultUserAgent = defaultUserAgent;
mRequestIds = new HashSet<>();
}

private OkHttpClient createOkHttpClient(
@Nullable OkHttpClientProvider httpClientProvider,
@Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
OkHttpClient client = httpClientProvider == null ? new DefaultOkHttpProvider().get() : httpClientProvider.get();
if (networkInterceptorCreators != null) {
OkHttpClient.Builder clientBuilder = client.newBuilder();
for (NetworkInterceptorCreator networkInterceptorCreator : networkInterceptorCreators) {
clientBuilder.addNetworkInterceptor(networkInterceptorCreator.create());
}
client = clientBuilder.build();
}
mClient = client;
mCookieHandler = new ForwardingCookieHandler(reactContext);
mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
mShuttingDown = false;
mDefaultUserAgent = defaultUserAgent;
mRequestIds = new HashSet<>();
return client;
}

/**
* @param context the ReactContext of the application
* @param defaultUserAgent the User-Agent header that will be set for all requests where the
* caller does not provide one explicitly
* @param client the {@link OkHttpClient} to be used for networking
* @param httpClientProvider {@link OkHttpClientProvider} to provider {@link OkHttpClient}
*/
/* package */ NetworkingModule(
ReactApplicationContext context,
@Nullable String defaultUserAgent,
OkHttpClient client) {
this(context, defaultUserAgent, client, null);
@Nullable OkHttpClientProvider httpClientProvider) {
this(context, defaultUserAgent, httpClientProvider, null);
}

/**
* @param context the ReactContext of the application
*/
public NetworkingModule(final ReactApplicationContext context) {
this(context, null, OkHttpClientProvider.createClient(), null);
this(context, null, null, null);
}

/**
* @param context the ReactContext of the application
* @param httpClientProvider {@link OkHttpClientProvider} to provide {@link OkHttpClient}
*/
public NetworkingModule(final ReactApplicationContext context, @Nullable OkHttpClientProvider httpClientProvider) {
this(context, null, httpClientProvider, null);
}

/**
Expand All @@ -123,7 +138,7 @@ public NetworkingModule(final ReactApplicationContext context) {
public NetworkingModule(
ReactApplicationContext context,
List<NetworkInterceptorCreator> networkInterceptorCreators) {
this(context, null, OkHttpClientProvider.createClient(), networkInterceptorCreators);
this(context, null, null, networkInterceptorCreators);
}

/**
Expand All @@ -132,7 +147,7 @@ public NetworkingModule(
* caller does not provide one explicitly
*/
public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
this(context, defaultUserAgent, OkHttpClientProvider.createClient(), null);
this(context, defaultUserAgent, new DefaultOkHttpProvider(), null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,11 @@

package com.facebook.react.modules.network;

import android.os.Build;

import com.facebook.common.logging.FLog;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;

/**
* Helper class that provides the same OkHttpClient instance that will be used for all networking
* requests.
* Interface to provide OkHttpClient instance that will be used for all networking requests.
*/
public class OkHttpClientProvider {

// Centralized OkHttpClient for all networking requests.
private static @Nullable OkHttpClient sClient;

public static OkHttpClient getOkHttpClient() {
if (sClient == null) {
sClient = createClient();
}
return sClient;
}

// okhttp3 OkHttpClient is immutable
// This allows app to init an OkHttpClient with custom settings.
public static void replaceOkHttpClient(OkHttpClient client) {
sClient = client;
}

public static OkHttpClient createClient() {
// No timeouts by default
OkHttpClient.Builder client = new OkHttpClient.Builder()
.connectTimeout(0, TimeUnit.MILLISECONDS)
.readTimeout(0, TimeUnit.MILLISECONDS)
.writeTimeout(0, TimeUnit.MILLISECONDS)
.cookieJar(new ReactCookieJarContainer());

return enableTls12OnPreLollipop(client).build();
}

/*
On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are
available but not enabled by default. The following method
enables it.
*/
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
client.sslSocketFactory(new TLSSocketFactory());

ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();

List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);

client.connectionSpecs(specs);
} catch (Exception exc) {
FLog.e("OkHttpClientProvider", "Error while enabling TLS 1.2", exc);
}
}

return client;
}

public interface OkHttpClientProvider {
OkHttpClient get();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,52 @@
package com.facebook.react.shell;

import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.react.modules.network.DefaultOkHttpProvider;
import com.facebook.react.modules.network.OkHttpClientProvider;

import javax.annotation.Nullable;

/**
* Configuration for {@link MainReactPackage}
*/
public class MainPackageConfig {

private ImagePipelineConfig mFrescoConfig;
private final @Nullable OkHttpClientProvider mHttpClientProvider;

private final ImagePipelineConfig mFrescoConfig;

private MainPackageConfig(Builder builder) {
mFrescoConfig = builder.mFrescoConfig;
mHttpClientProvider = builder.mHttpClientProvider;
}

public ImagePipelineConfig getFrescoConfig() {
return mFrescoConfig;
}

public OkHttpClientProvider getHttpClientProvider() {
return mHttpClientProvider != null ? mHttpClientProvider : new DefaultOkHttpProvider();
}

public static class Builder {

private ImagePipelineConfig mFrescoConfig;
private OkHttpClientProvider mHttpClientProvider;

public Builder setFrescoConfig(ImagePipelineConfig frescoConfig) {
mFrescoConfig = frescoConfig;
return this;
}

/**
* Option to inject configured OkHttpClient to react-native networking subsystem.
* This will be used only by the fetch networking calls (Fresco library, for eg, will not use this client).
*/
public Builder setOkHttpClientProvider(OkHttpClientProvider okHttpClientProvider) {
this.mHttpClientProvider = okHttpClientProvider;
return this;
}

public MainPackageConfig build() {
return new MainPackageConfig(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import android.content.SharedPreferences;
import android.preference.PreferenceManager;

import com.facebook.react.LazyReactPackage;
import com.facebook.react.animated.NativeAnimatedModule;
import com.facebook.react.bridge.JavaScriptModule;
Expand Down Expand Up @@ -75,12 +76,13 @@
import com.facebook.react.views.viewpager.ReactViewPagerManager;
import com.facebook.react.views.webview.ReactWebViewManager;

import javax.inject.Provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.inject.Provider;

/**
* Package defining basic modules and view managers.
*/
Expand Down Expand Up @@ -194,7 +196,9 @@ public NativeModule get() {
new ModuleSpec(NetworkingModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetworkingModule(context);
return mConfig == null ?
new NetworkingModule(context) :
new NetworkingModule(context, mConfig.getHttpClientProvider());
}
}),
new ModuleSpec(NetInfoModule.class, new Provider<NativeModule>() {
Expand Down
Loading

0 comments on commit 71217d1

Please sign in to comment.