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 19, 2017
1 parent 1dd7bc1 commit dd205a1
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 123 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 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.
*/
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;
}
}
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 okHttpClientProvider,
@Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
super(reactContext);

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

private OkHttpClient createOkHttpClient(
OkHttpClientProvider okHttpClientProvider,
@Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
OkHttpClient client = okHttpClientProvider == null ? new DefaultOkHttpProvider().get() : okHttpClientProvider.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 okHttpClientProvider the {@link OkHttpClient} provider to be used for networking
*/
/* package */ NetworkingModule(
ReactApplicationContext context,
@Nullable String defaultUserAgent,
OkHttpClient client) {
this(context, defaultUserAgent, client, null);
OkHttpClientProvider okHttpClientProvider) {
this(context, defaultUserAgent, okHttpClientProvider, 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 okHttpClientProvider to provide OkHttpInstance with different settings
*/
public NetworkingModule(final ReactApplicationContext context, OkHttpClientProvider okHttpClientProvider) {
this(context, null, okHttpClientProvider, 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 that provides the 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,51 @@
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;

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

private ImagePipelineConfig mFrescoConfig;

private final OkHttpClientProvider mOkHttpClientProvider;

private final ImagePipelineConfig mFrescoConfig;

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

public ImagePipelineConfig getFrescoConfig() {
return mFrescoConfig;
}

public OkHttpClientProvider getOkHttpClientProvider() {
return mOkHttpClientProvider != null ? mOkHttpClientProvider : new DefaultOkHttpProvider();
}

public static class Builder {

private ImagePipelineConfig mFrescoConfig;
private OkHttpClientProvider mOkHttpClientProvider;

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

/**
* Ability to inject configured OkHttpClient provider to react-native networking subsystem.
* This will be very useful, especially in brownfield applications.
*/
public Builder setOkHttpClientProvider(OkHttpClientProvider okHttpClientProvider) {
this.mOkHttpClientProvider = okHttpClientProvider;
return this;
}

public MainPackageConfig build() {
return new MainPackageConfig(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ 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.getOkHttpClientProvider());
}
}),
new ModuleSpec(NetInfoModule.class, new Provider<NativeModule>() {
Expand Down
Loading

0 comments on commit dd205a1

Please sign in to comment.