Use ConcurrentHashMap for handling concurrent Android websockets, and…

Summary:
… prevent unknown websocket IDs from crashing on Android (show warning on development builds instead)

This PR addresses #3346; an unknown websocket ID should produce a warning during development, but not cause crashes in production RN apps. This PR was created by satya164's request, and was inspired by tanthanh289's suggestion on #3346's thread.

On Android, create a websocket using a service like Pusher (`pusher-js` npm package) or manually, and then induce removal of its websocket ID. Result should be a red warning screen during development, and no crash in the app's release variant.

 [ANDROID] [BUGFIX] [WebSocket] - Prevent unknown websocket IDs from crashing on Android
Closes https://github.com/facebook/react-native/pull/17884

Differential Revision: D6954038

Pulled By: hramos

fbshipit-source-id: b346d80d7568996b8819c0de54552abb534cbfae
This commit is contained in:
Wei Sun 2018-02-09 14:29:27 -08:00 committed by Facebook Github Bot
parent 01a58d182a
commit 1a790f8703
1 changed files with 55 additions and 10 deletions

View File

@ -27,6 +27,7 @@ import com.facebook.react.modules.network.ForwardingCookieHandler;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -48,8 +49,8 @@ public final class WebSocketModule extends ReactContextBaseJavaModule {
void onMessage(ByteString byteString, WritableMap params);
}
private final Map<Integer, WebSocket> mWebSocketConnections = new HashMap<>();
private final Map<Integer, ContentHandler> mContentHandlers = new HashMap<>();
private final Map<Integer, WebSocket> mWebSocketConnections = new ConcurrentHashMap<>();
private final Map<Integer, ContentHandler> mContentHandlers = new ConcurrentHashMap<>();
private ReactContext mReactContext;
private ForwardingCookieHandler mCookieHandler;
@ -224,8 +225,19 @@ public final class WebSocketModule extends ReactContextBaseJavaModule {
public void send(String message, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
// This is a programmer error -- display development warning
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("message", "client is null");
sendEvent("websocketFailed", params);
params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", 0);
params.putString("reason", "client is null");
sendEvent("websocketClosed", params);
mWebSocketConnections.remove(id);
mContentHandlers.remove(id);
return;
}
try {
client.send(message);
@ -238,8 +250,19 @@ public final class WebSocketModule extends ReactContextBaseJavaModule {
public void sendBinary(String base64String, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
// This is a programmer error -- display development warning
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("message", "client is null");
sendEvent("websocketFailed", params);
params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", 0);
params.putString("reason", "client is null");
sendEvent("websocketClosed", params);
mWebSocketConnections.remove(id);
mContentHandlers.remove(id);
return;
}
try {
client.send(ByteString.decodeBase64(base64String));
@ -251,8 +274,19 @@ public final class WebSocketModule extends ReactContextBaseJavaModule {
public void sendBinary(ByteString byteString, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
// This is a programmer error -- display development warning
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("message", "client is null");
sendEvent("websocketFailed", params);
params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", 0);
params.putString("reason", "client is null");
sendEvent("websocketClosed", params);
mWebSocketConnections.remove(id);
mContentHandlers.remove(id);
return;
}
try {
client.send(byteString);
@ -265,8 +299,19 @@ public final class WebSocketModule extends ReactContextBaseJavaModule {
public void ping(int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
// This is a programmer error -- display development warning
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("message", "client is null");
sendEvent("websocketFailed", params);
params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", 0);
params.putString("reason", "client is null");
sendEvent("websocketClosed", params);
mWebSocketConnections.remove(id);
mContentHandlers.remove(id);
return;
}
try {
client.send(ByteString.EMPTY);