Skip to content

Commit c7258c3

Browse files
committed
networking: Fix requests failing when turning network off and on facebook#19709.
This bug is probably actually a bug in OkHttp: square/okhttp#4079 Both issues linked above contain extensive details about the issue, its likely origins and how to reproduce it. A short summary of the issue and the fix in this commit: On Android, disconnecting from the network somehow corrupts the idle connections and ongoing calls in okhttp clients. New requests made over these clients fail. This commit works around that bug by evicting the idle connection pool and cancelling all ongoing calls of each client when we receive a DISCONNECTED or CONNECTING event (we don't know yet if only one or both of them cause the issue). Cancelling all calls is aggressive, but when a device disconnects any ongoing calls can fail anyway, so an app has to expect this scenario.
1 parent 370bcff commit c7258c3

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java

+8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99

1010
import javax.annotation.Nullable;
1111

12+
import android.Manifest;
1213
import android.app.Activity;
1314
import android.content.Intent;
15+
import android.content.pm.PackageManager;
1416
import android.os.Bundle;
17+
import android.support.v4.content.ContextCompat;
1518
import android.view.KeyEvent;
1619

1720
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
1821
import com.facebook.react.modules.core.PermissionAwareActivity;
1922
import com.facebook.react.modules.core.PermissionListener;
23+
import com.facebook.react.modules.network.OkHttpClientProvider;
2024

2125
/**
2226
* Base Activity for React Native applications.
@@ -50,6 +54,10 @@ protected ReactActivityDelegate createReactActivityDelegate() {
5054
protected void onCreate(Bundle savedInstanceState) {
5155
super.onCreate(savedInstanceState);
5256
mDelegate.onCreate(savedInstanceState);
57+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE)
58+
== PackageManager.PERMISSION_GRANTED) {
59+
OkHttpClientProvider.addNetworkListenerToEvictIdleConnectionsOnNetworkChange(getApplicationContext());
60+
}
5361
}
5462

5563
@Override

ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java

+63-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,25 @@
77

88
package com.facebook.react.modules.network;
99

10+
import android.content.BroadcastReceiver;
11+
import android.content.Context;
12+
import android.content.Intent;
13+
import android.content.IntentFilter;
14+
import android.net.ConnectivityManager;
15+
import android.net.NetworkInfo;
16+
import android.os.AsyncTask;
1017
import android.os.Build;
18+
import android.os.Bundle;
1119

1220
import com.facebook.common.logging.FLog;
21+
import com.facebook.react.ReactActivity;
22+
import com.facebook.react.bridge.ReactApplicationContext;
1323

1424
import java.util.ArrayList;
25+
import java.util.Collections;
1526
import java.util.List;
27+
import java.util.Set;
28+
import java.util.WeakHashMap;
1629
import java.util.concurrent.TimeUnit;
1730

1831
import javax.annotation.Nullable;
@@ -33,6 +46,9 @@ public class OkHttpClientProvider {
3346
// User-provided OkHttpClient factory
3447
private static @Nullable OkHttpClientFactory sFactory;
3548

49+
private final static Set<OkHttpClient> sClients = Collections.newSetFromMap(
50+
new WeakHashMap<OkHttpClient, Boolean>());
51+
3652
public static void setOkHttpClientFactory(OkHttpClientFactory factory) {
3753
sFactory = factory;
3854
}
@@ -43,6 +59,44 @@ public static OkHttpClient getOkHttpClient() {
4359
}
4460
return sClient;
4561
}
62+
63+
private static class EvictIdleConnectionsTask extends AsyncTask {
64+
@Override
65+
protected Object doInBackground(Object[] objects) {
66+
for (OkHttpClient client: sClients) {
67+
client.connectionPool().evictAll();
68+
client.dispatcher().cancelAll();
69+
}
70+
return null;
71+
}
72+
}
73+
74+
/*
75+
See https://github.com/facebook/react-native/issues/19709 for context.
76+
We know that idle connections get corrupted when the connectivity state
77+
changes to disconnected, but the debugging of this issue hasn't been
78+
exhaustive and it's possible that other changes in connectivity also
79+
corrupt idle connections. `CONNECTIVITY_ACTION`s occur infrequently
80+
enough to go safe and evict all idle connections on every such action.
81+
*/
82+
public static void addNetworkListenerToEvictIdleConnectionsOnNetworkChange(Context context) {
83+
final BroadcastReceiver br = new BroadcastReceiver() {
84+
@Override
85+
public void onReceive(Context context, Intent intent) {
86+
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
87+
Bundle extras = intent.getExtras();
88+
NetworkInfo info = extras.getParcelable("networkInfo");
89+
NetworkInfo.State state = info.getState();
90+
if (state != NetworkInfo.State.CONNECTED) {
91+
new EvictIdleConnectionsTask().execute();
92+
}
93+
}
94+
}
95+
};
96+
final IntentFilter intentFilter = new IntentFilter();
97+
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
98+
context.registerReceiver(br, intentFilter);
99+
}
46100

47101
// okhttp3 OkHttpClient is immutable
48102
// This allows app to init an OkHttpClient with custom settings.
@@ -54,7 +108,11 @@ public static OkHttpClient createClient() {
54108
if (sFactory != null) {
55109
return sFactory.createNewNetworkModuleClient();
56110
}
57-
return createClientBuilder().build();
111+
else {
112+
final OkHttpClient client = createClientBuilder().build();
113+
registerClientToEvictIdleConnectionsOnNetworkChange(client);
114+
return client;
115+
}
58116
}
59117

60118
public static OkHttpClient.Builder createClientBuilder() {
@@ -68,6 +126,10 @@ public static OkHttpClient.Builder createClientBuilder() {
68126
return enableTls12OnPreLollipop(client);
69127
}
70128

129+
public static void registerClientToEvictIdleConnectionsOnNetworkChange(OkHttpClient client) {
130+
sClients.add(client);
131+
}
132+
71133
/*
72134
On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are
73135
available but not enabled by default. The following method

0 commit comments

Comments
 (0)