From c1443823162fb83d5a938d52dd89f4e024076b2a Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Mon, 3 Jun 2024 06:04:50 -0700 Subject: [PATCH] Remove legacy InspectorPackagerConnection (#44746) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44746 Cleanup after this experiment's removal in D57730921. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D58017460 fbshipit-source-id: f9328875d6ef9a76599ae560c58cef6caab87a57 --- .../DevSupport/RCTInspectorDevServerHelper.mm | 8 +- .../RCTInspectorPackagerConnection.m | 325 ------------------ .../ReactAndroid/api/ReactAndroid.api | 7 - .../react/devsupport/DevServerHelper.java | 10 +- .../InspectorPackagerConnection.java | 314 ----------------- 5 files changed, 3 insertions(+), 661 deletions(-) delete mode 100644 packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java diff --git a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm index 764bad96807..34036a24962 100644 --- a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm +++ b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm @@ -14,7 +14,6 @@ #import #import -#import #import #import @@ -181,12 +180,7 @@ static void sendEventToAllConnections(NSString *event) NSString *key = [inspectorURL absoluteString]; id connection = socketConnections[key]; if (!connection || !connection.isConnected) { - if (facebook::react::jsinspector_modern::InspectorFlags::getInstance().getFuseboxEnabled()) { - connection = [[RCTCxxInspectorPackagerConnection alloc] initWithURL:inspectorURL]; - } else { - // TODO(T190163403): Remove legacy RCTInspectorPackagerConnection - connection = [[RCTInspectorPackagerConnection alloc] initWithURL:inspectorURL]; - } + connection = [[RCTCxxInspectorPackagerConnection alloc] initWithURL:inspectorURL]; socketConnections[key] = connection; [connection connect]; diff --git a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m b/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m deleted file mode 100644 index 7d934b43343..00000000000 --- a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#if RCT_DEV || RCT_REMOTE_PROFILE - -#import -#import -#import -#import -#import - -// This is a port of the Android impl, at -// ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java -// please keep consistent :) - -const int RECONNECT_DELAY_MS = 2000; - -@interface RCTInspectorPackagerConnection () { - NSURL *_url; - NSMutableDictionary *_inspectorConnections; - SRWebSocket *_webSocket; - BOOL _closed; - BOOL _suppressConnectionErrors; -} -@end - -@interface RCTInspectorRemoteConnection () { - __weak RCTInspectorPackagerConnection *_owningPackagerConnection; - NSString *_pageId; -} -- (instancetype)initWithPackagerConnection:(RCTInspectorPackagerConnection *)owningPackagerConnection - pageId:(NSString *)pageId; -@end - -static NSDictionary *makePageIdPayload(NSString *pageId) -{ - return @{@"pageId" : pageId}; -} - -@implementation RCTInspectorPackagerConnection - -RCT_NOT_IMPLEMENTED(-(instancetype)init) - -- (instancetype)initWithURL:(NSURL *)url -{ - if (self = [super init]) { - _url = url; - _inspectorConnections = [NSMutableDictionary new]; - } - return self; -} - -- (void)handleProxyMessage:(NSDictionary *)message -{ - NSString *event = message[@"event"]; - NSDictionary *payload = message[@"payload"]; - if ([@"getPages" isEqualToString:event]) { - [self sendEvent:event payload:[self pages]]; - } else if ([@"wrappedEvent" isEqualToString:event]) { - [self handleWrappedEvent:payload]; - } else if ([@"connect" isEqualToString:event]) { - [self handleConnect:payload]; - } else if ([@"disconnect" isEqualToString:event]) { - [self handleDisconnect:payload]; - } else { - RCTLogError(@"Unknown event: %@", event); - } -} - -- (void)sendEventToAllConnections:(NSString *)event -{ - for (NSString *pageId in _inspectorConnections) { - [_inspectorConnections[pageId] sendMessage:event]; - } -} - -- (void)closeAllConnections -{ - for (NSString *pageId in _inspectorConnections) { - [[_inspectorConnections objectForKey:pageId] disconnect]; - } - [_inspectorConnections removeAllObjects]; -} - -- (void)handleConnect:(NSDictionary *)payload -{ - NSString *pageId = payload[@"pageId"]; - RCTInspectorLocalConnection *existingConnection = _inspectorConnections[pageId]; - if (existingConnection) { - [_inspectorConnections removeObjectForKey:pageId]; - [existingConnection disconnect]; - RCTLogWarn(@"Already connected: %@", pageId); - return; - } - - RCTInspectorRemoteConnection *remoteConnection = - [[RCTInspectorRemoteConnection alloc] initWithPackagerConnection:self pageId:pageId]; - - RCTInspectorLocalConnection *inspectorConnection = [RCTInspector connectPage:[pageId integerValue] - forRemoteConnection:remoteConnection]; - _inspectorConnections[pageId] = inspectorConnection; -} - -- (void)handleDisconnect:(NSDictionary *)payload -{ - NSString *pageId = payload[@"pageId"]; - RCTInspectorLocalConnection *inspectorConnection = _inspectorConnections[pageId]; - if (inspectorConnection) { - [self removeConnectionForPage:pageId]; - [inspectorConnection disconnect]; - } -} - -- (void)removeConnectionForPage:(NSString *)pageId -{ - [_inspectorConnections removeObjectForKey:pageId]; -} - -- (void)handleWrappedEvent:(NSDictionary *)payload -{ - NSString *pageId = payload[@"pageId"]; - NSString *wrappedEvent = payload[@"wrappedEvent"]; - RCTInspectorLocalConnection *inspectorConnection = _inspectorConnections[pageId]; - if (!inspectorConnection) { - RCTLogWarn(@"Not connected to page: %@ , failed trying to handle event: %@", pageId, wrappedEvent); - return; - } - [inspectorConnection sendMessage:wrappedEvent]; -} - -- (NSArray *)pages -{ - NSArray *pages = [RCTInspector pages]; - NSMutableArray *array = [NSMutableArray arrayWithCapacity:pages.count]; - - for (RCTInspectorPage *page in pages) { - NSDictionary *jsonPage = @{ - @"id" : [@(page.id) stringValue], - @"title" : page.title, - @"app" : [[NSBundle mainBundle] bundleIdentifier], - @"vm" : page.vm, - }; - [array addObject:jsonPage]; - } - return array; -} - -- (void)sendWrappedEvent:(NSString *)pageId message:(NSString *)message -{ - NSDictionary *payload = @{ - @"pageId" : pageId, - @"wrappedEvent" : message, - }; - [self sendEvent:@"wrappedEvent" payload:payload]; -} - -- (void)sendEvent:(NSString *)name payload:(id)payload -{ - NSDictionary *jsonMessage = @{ - @"event" : name, - @"payload" : payload, - }; - [self sendToPackager:jsonMessage]; -} - -// analogous to InspectorPackagerConnection.Connection.onFailure(...) -- (void)webSocket:(__unused SRWebSocket *)webSocket didFailWithError:(NSError *)error -{ - if (_webSocket) { - [self abort:@"Websocket exception" withCause:error]; - } - if (!_closed && [error code] != ECONNREFUSED) { - [self reconnect]; - } -} - -// analogous to InspectorPackagerConnection.Connection.onMessage(...) -- (void)webSocket:(__unused SRWebSocket *)webSocket didReceiveMessageWithString:(nonnull NSString *)messageText -{ - NSError *error = nil; - id parsedJSON = RCTJSONParse(messageText, &error); - if (error) { - RCTLogWarn(@"Unrecognized inspector message, string was not valid JSON: %@", messageText); - return; - } - - [self handleProxyMessage:parsedJSON]; -} - -// analogous to InspectorPackagerConnection.Connection.onClosed(...) -- (void)webSocket:(__unused SRWebSocket *)webSocket - didCloseWithCode:(__unused NSInteger)code - reason:(__unused NSString *)reason - wasClean:(__unused BOOL)wasClean -{ - _webSocket = nil; - [self closeAllConnections]; - if (!_closed) { - [self reconnect]; - } -} - -- (bool)isConnected -{ - return _webSocket != nil; -} - -- (void)connect -{ - if (_closed) { - RCTLogError(@"Illegal state: Can't connect after having previously been closed."); - return; - } - - // The corresponding android code has a lot of custom config options for - // timeouts, but our previous class, RCTSRWebSocket didn't have the same - // implemented options. Might be worth reinvestigating for SRWebSocket? - _webSocket = [[SRWebSocket alloc] initWithURL:_url]; - _webSocket.delegate = self; - [_webSocket open]; -} - -- (void)reconnect -{ - if (_closed) { - RCTLogError(@"Illegal state: Can't reconnect after having previously been closed."); - return; - } - - if (_suppressConnectionErrors) { - RCTLogWarn(@"Couldn't connect to packager, will silently retry"); - _suppressConnectionErrors = true; - } - - __weak RCTInspectorPackagerConnection *weakSelf = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, RECONNECT_DELAY_MS * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - RCTInspectorPackagerConnection *strongSelf = weakSelf; - if (strongSelf && !strongSelf->_closed) { - [strongSelf connect]; - } - }); -} - -- (void)closeQuietly -{ - _closed = true; - [self disposeWebSocket]; -} - -- (void)sendToPackager:(NSDictionary *)messageObject -{ - __weak RCTInspectorPackagerConnection *weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - RCTInspectorPackagerConnection *strongSelf = weakSelf; - if (strongSelf && !strongSelf->_closed) { - NSError *error; - NSString *messageText = RCTJSONStringify(messageObject, &error); - if (error) { - RCTLogWarn(@"Couldn't send event to packager: %@", error); - } else { - [strongSelf->_webSocket sendString:messageText error:nil]; - } - } - }); -} - -- (void)abort:(NSString *)message withCause:(NSError *)cause -{ - // Don't log ECONNREFUSED at all; it's expected in cases where the server isn't listening. - if (![cause.domain isEqual:NSPOSIXErrorDomain] || cause.code != ECONNREFUSED) { - RCTLogInfo(@"Error occurred, shutting down websocket connection: %@ %@", message, cause); - } - - [self closeAllConnections]; - [self disposeWebSocket]; -} - -- (void)disposeWebSocket -{ - if (_webSocket) { - [_webSocket closeWithCode:1000 reason:@"End of session"]; - _webSocket.delegate = nil; - _webSocket = nil; - } -} - -@end - -@implementation RCTInspectorRemoteConnection - -RCT_NOT_IMPLEMENTED(-(instancetype)init) - -- (instancetype)initWithPackagerConnection:(RCTInspectorPackagerConnection *)owningPackagerConnection - pageId:(NSString *)pageId -{ - if (self = [super init]) { - _owningPackagerConnection = owningPackagerConnection; - _pageId = pageId; - } - return self; -} - -- (void)onMessage:(NSString *)message -{ - [_owningPackagerConnection sendWrappedEvent:_pageId message:message]; -} - -- (void)onDisconnect -{ - RCTInspectorPackagerConnection *owningPackagerConnectionStrong = _owningPackagerConnection; - if (owningPackagerConnectionStrong) { - [owningPackagerConnectionStrong removeConnectionForPage:_pageId]; - [owningPackagerConnectionStrong sendEvent:@"disconnect" payload:makePageIdPayload(_pageId)]; - } -} - -@end - -#endif diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 03d6e5bf89b..28c4c940600 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2184,13 +2184,6 @@ public final class com/facebook/react/devsupport/InspectorFlags { public static final fun getFuseboxEnabled ()Z } -public class com/facebook/react/devsupport/InspectorPackagerConnection : com/facebook/react/devsupport/IInspectorPackagerConnection { - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun closeQuietly ()V - public fun connect ()V - public fun sendEventToAllConnections (Ljava/lang/String;)V -} - public class com/facebook/react/devsupport/JSCHeapCapture : com/facebook/fbreact/specs/NativeJSCHeapCaptureSpec { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun captureComplete (Ljava/lang/String;Ljava/lang/String;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index dcb4dcca585..0a478c9828b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -210,14 +210,8 @@ public class DevServerHelper { new AsyncTask() { @Override protected Void doInBackground(Void... params) { - if (InspectorFlags.getFuseboxEnabled()) { - mInspectorPackagerConnection = - new CxxInspectorPackagerConnection(getInspectorDeviceUrl(), mPackageName); - } else { - // TODO(T190163403): Remove legacy InspectorPackagerConnection - mInspectorPackagerConnection = - new InspectorPackagerConnection(getInspectorDeviceUrl(), mPackageName); - } + mInspectorPackagerConnection = + new CxxInspectorPackagerConnection(getInspectorDeviceUrl(), mPackageName); mInspectorPackagerConnection.connect(); return null; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java deleted file mode 100644 index 84107fb5e4c..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.devsupport; - -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.Inspector; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public class InspectorPackagerConnection implements IInspectorPackagerConnection { - private static final String TAG = "InspectorPackagerConnection"; - - private final Connection mConnection; - private final Map mInspectorConnections; - private final String mPackageName; - - public InspectorPackagerConnection(String url, String packageName) { - mConnection = new Connection(url); - mInspectorConnections = new HashMap<>(); - mPackageName = packageName; - } - - public void connect() { - mConnection.connect(); - } - - public void closeQuietly() { - mConnection.close(); - } - - public void sendEventToAllConnections(String event) { - for (Map.Entry inspectorConnectionEntry : - mInspectorConnections.entrySet()) { - Inspector.LocalConnection inspectorConnection = inspectorConnectionEntry.getValue(); - inspectorConnection.sendMessage(event); - } - } - - void handleProxyMessage(JSONObject message) throws JSONException, IOException { - String event = message.getString("event"); - switch (event) { - case "getPages": - sendEvent("getPages", getPages()); - break; - case "wrappedEvent": - handleWrappedEvent(message.getJSONObject("payload")); - break; - case "connect": - handleConnect(message.getJSONObject("payload")); - break; - case "disconnect": - handleDisconnect(message.getJSONObject("payload")); - break; - default: - throw new IllegalArgumentException("Unknown event: " + event); - } - } - - void closeAllConnections() { - for (Map.Entry entry : mInspectorConnections.entrySet()) { - entry.getValue().disconnect(); - } - mInspectorConnections.clear(); - } - - private void handleConnect(JSONObject payload) throws JSONException { - final String pageId = payload.getString("pageId"); - Inspector.LocalConnection inspectorConnection = mInspectorConnections.remove(pageId); - if (inspectorConnection != null) { - throw new IllegalStateException("Already connected: " + pageId); - } - - try { - // TODO: Use strings for id's too - inspectorConnection = - Inspector.connect( - Integer.parseInt(pageId), - new Inspector.RemoteConnection() { - @Override - public void onMessage(String message) { - try { - sendWrappedEvent(pageId, message); - } catch (JSONException e) { - FLog.w(TAG, "Couldn't send event to packager", e); - } - } - - @Override - public void onDisconnect() { - try { - mInspectorConnections.remove(pageId); - sendEvent("disconnect", makePageIdPayload(pageId)); - } catch (JSONException e) { - FLog.w(TAG, "Couldn't send event to packager", e); - } - } - }); - mInspectorConnections.put(pageId, inspectorConnection); - } catch (Exception e) { - FLog.w(TAG, "Failed to open page: " + pageId, e); - sendEvent("disconnect", makePageIdPayload(pageId)); - } - } - - private void handleDisconnect(JSONObject payload) throws JSONException { - final String pageId = payload.getString("pageId"); - Inspector.LocalConnection inspectorConnection = mInspectorConnections.remove(pageId); - if (inspectorConnection == null) { - return; - } - - inspectorConnection.disconnect(); - } - - private void handleWrappedEvent(JSONObject payload) throws JSONException { - final String pageId = payload.getString("pageId"); - String wrappedEvent = payload.getString("wrappedEvent"); - Inspector.LocalConnection inspectorConnection = mInspectorConnections.get(pageId); - if (inspectorConnection == null) { - // This tends to happen during reloads, so don't panic. - FLog.w(TAG, "PageID " + pageId + " is disconnected. Dropping event: " + wrappedEvent); - return; - } - inspectorConnection.sendMessage(wrappedEvent); - } - - private JSONArray getPages() throws JSONException { - List pages = Inspector.getPages(); - JSONArray array = new JSONArray(); - for (Inspector.Page page : pages) { - JSONObject jsonPage = new JSONObject(); - jsonPage.put("id", String.valueOf(page.getId())); - jsonPage.put("title", page.getTitle()); - jsonPage.put("app", mPackageName); - jsonPage.put("vm", page.getVM()); - array.put(jsonPage); - } - return array; - } - - private void sendWrappedEvent(String pageId, String message) throws JSONException { - JSONObject payload = new JSONObject(); - payload.put("pageId", pageId); - payload.put("wrappedEvent", message); - sendEvent("wrappedEvent", payload); - } - - private void sendEvent(String name, Object payload) throws JSONException { - JSONObject jsonMessage = new JSONObject(); - jsonMessage.put("event", name); - jsonMessage.put("payload", payload); - mConnection.send(jsonMessage); - } - - private JSONObject makePageIdPayload(String pageId) throws JSONException { - JSONObject payload = new JSONObject(); - payload.put("pageId", pageId); - return payload; - } - - private class Connection extends WebSocketListener { - private static final int RECONNECT_DELAY_MS = 2000; - - private final String mUrl; - - private OkHttpClient mHttpClient; - private @Nullable WebSocket mWebSocket; - private final Handler mHandler; - private boolean mClosed; - private boolean mSuppressConnectionErrors; - - public Connection(String url) { - mUrl = url; - mHandler = new Handler(Looper.getMainLooper()); - } - - @Override - public void onOpen(WebSocket webSocket, Response response) { - mWebSocket = webSocket; - } - - @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - if (mWebSocket != null) { - abort("Websocket exception", t); - } - if (!mClosed) { - reconnect(); - } - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - try { - handleProxyMessage(new JSONObject(text)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void onClosed(WebSocket webSocket, int code, String reason) { - mWebSocket = null; - closeAllConnections(); - if (!mClosed) { - reconnect(); - } - } - - public void connect() { - if (mClosed) { - throw new IllegalStateException("Can't connect closed client"); - } - if (mHttpClient == null) { - mHttpClient = - new OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build(); - } - - Request request = new Request.Builder().url(mUrl).build(); - mHttpClient.newWebSocket(request, this); - } - - private void reconnect() { - if (mClosed) { - throw new IllegalStateException("Can't reconnect closed client"); - } - if (!mSuppressConnectionErrors) { - FLog.w(TAG, "Couldn't connect to packager, will silently retry"); - mSuppressConnectionErrors = true; - } - mHandler.postDelayed( - new Runnable() { - @Override - public void run() { - // check that we haven't been closed in the meantime - if (!mClosed) { - connect(); - } - } - }, - RECONNECT_DELAY_MS); - } - - public void close() { - mClosed = true; - if (mWebSocket != null) { - try { - mWebSocket.close(1000, "End of session"); - } catch (Exception e) { - // swallow, no need to handle it here - } - mWebSocket = null; - } - } - - public void send(final JSONObject object) { - new AsyncTask() { - @Override - protected Void doInBackground(WebSocket... sockets) { - if (sockets == null || sockets.length == 0) { - return null; - } - try { - sockets[0].send(object.toString()); - } catch (Exception e) { - FLog.w(TAG, "Couldn't send event to packager", e); - } - return null; - } - }.execute(mWebSocket); - } - - private void abort(String message, Throwable cause) { - FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause); - closeAllConnections(); - closeWebSocketQuietly(); - } - - private void closeWebSocketQuietly() { - if (mWebSocket != null) { - try { - mWebSocket.close(1000, "End of session"); - } catch (Exception e) { - // swallow, no need to handle it here - } - mWebSocket = null; - } - } - } -}