Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject configured OkHttpClient to networking module using MainPackageConfig #14068

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
Copy link
Contributor

@mkonicek mkonicek May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If fetching images should not be affected by the HTTP provider from MainPackageConfig we should document the MainPackageConfig only affects the fetch API.

It's probably easier, however, to just use the HTTP client configured in MainPackageConfig also here (for fetching images) to avoid confusion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked in to the details of how Fresco is using this client. But I guess, Fresco might need its own customised client configuration.

For now, I will add a comment in MainPackageConfig. Will take a look at this later. Would be great if someone with Fresco context can share some light.

Copy link
Contributor

@mkonicek mkonicek Jun 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK sounds good, someone else can do that separately and we can keep this pull request simpler.


// 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,63 @@
/**
* 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. This 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,
@Nullable 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use the {@link} annotation for the java docs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit @param context the {@link ReactApplicationContext} 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, null, null);
}

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

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 calls from JavaScript (especially from fetch API).
*/
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,57 @@
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 @Nullable OkHttpClientProvider getHttpClientProvider() {
return mHttpClientProvider;
}

public static class Builder {

private ImagePipelineConfig mFrescoConfig;
private @Nullable OkHttpClientProvider mHttpClientProvider;

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

/**
* Allows to provide a custom OkHttp client provider that will be used
* for networking calls from JavaScript (especially the fetch API).
* This can be useful for example in hybrid apps which have their own
* OkHttp client configured with custom headers, logging, etc.
*
* This does not configure the networking client the Image JavaScript
* component uses when fetching images.
*/
public Builder setHttpClientProvider(@Nullable OkHttpClientProvider httpClientProvider) {
this.mHttpClientProvider = httpClientProvider;
return this;
}

public MainPackageConfig build() {
return new MainPackageConfig(this);
}
Expand Down
Loading