From ad2f24fe3a78f5b9107d9e7ab0e190ef7978cd06 Mon Sep 17 00:00:00 2001
From: Wei Sun <sunw@alum.mit.edu>
Date: Tue, 6 Feb 2018 17:11:16 -0800
Subject: [PATCH] Use ConcurrentHashMap for handling concurrent Android
 websockets, and prevent unknown websocket IDs from crashing on Android (show
 warning on development builds instead)

---
 .../modules/websocket/WebSocketModule.java    | 65 ++++++++++++++++---
 1 file changed, 55 insertions(+), 10 deletions(-)

diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java
index 2bfae94e7a9428..c7b6c8bbe50d79 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java
@@ -27,6 +27,7 @@
 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 interface ContentHandler {
     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 void close(int code, String reason, int id) {
   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 void send(String message, int id) {
   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 void sendBinary(String base64String, int id) {
   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 void sendBinary(ByteString byteString, int id) {
   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);