PHP 7.4.33
Preview: RCTWebSocketExecutor.mm Size: 9.24 KB
/var/www/uibuilder.cmshelp.dk/httpdocs/node_modules/react-native/React/CoreModules/RCTWebSocketExecutor.mm
/*
 * 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 <React/RCTWebSocketExecutor.h>

#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>

#import <ReactCommon/RCTTurboModule.h>
#import <SocketRocket/SRWebSocket.h>

#import "CoreModulesPlugins.h"

#if RCT_DEV // Debug executors are only supported in dev mode

typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary<NSString *, id> *reply);

@interface RCTWebSocketExecutor () <SRWebSocketDelegate, RCTTurboModule>

@end

@implementation RCTWebSocketExecutor {
  SRWebSocket *_socket;
  dispatch_queue_t _jsQueue;
  NSMutableDictionary<NSNumber *, RCTWSMessageCallback> *_callbacks;
  dispatch_semaphore_t _socketOpenSemaphore;
  NSMutableDictionary<NSString *, NSString *> *_injectedObjects;
  NSURL *_url;
  NSError *_setupError;
}

RCT_EXPORT_MODULE()

@synthesize bridge = _bridge;

- (instancetype)initWithURL:(NSURL *)URL
{
  RCTAssertParam(URL);

  if ((self = [self init])) {
    _url = URL;
  }
  return self;
}

- (void)setUp
{
  if (!_url) {
    NSInteger port = [[[_bridge bundleURL] port] integerValue] ?: RCT_METRO_PORT;
    NSString *host = [[_bridge bundleURL] host] ?: @"localhost";
    NSString *URLString =
        [NSString stringWithFormat:@"http://%@:%lld/debugger-proxy?role=client", host, (long long)port];
    _url = [RCTConvert NSURL:URLString];
  }

  _jsQueue = dispatch_queue_create("com.facebook.react.WebSocketExecutor", DISPATCH_QUEUE_SERIAL);
  _socket = [[SRWebSocket alloc] initWithURL:_url];
  _socket.delegate = self;
  _callbacks = [NSMutableDictionary new];
  _injectedObjects = [NSMutableDictionary new];
  [_socket setDelegateDispatchQueue:_jsQueue];

  NSURL *startDevToolsURL = [NSURL URLWithString:@"/launch-js-devtools" relativeToURL:_url];

  NSURLSession *session = [NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask =
      [session dataTaskWithRequest:[NSURLRequest requestWithURL:startDevToolsURL]
                 completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
                 }];
  [dataTask resume];
  if (![self connectToProxy]) {
    [self invalidate];
    NSString *error = [NSString stringWithFormat:
                                    @"Connection to %@ timed out. Are you "
                                     "running node proxy? If you are running on the device, check if "
                                     "you have the right IP address in `RCTWebSocketExecutor.m`.",
                                    _url];
    _setupError = RCTErrorWithMessage(error);
    RCTFatal(_setupError);
    return;
  }

  NSInteger retries = 3;
  BOOL runtimeIsReady = [self prepareJSRuntime];
  while (!runtimeIsReady && retries > 0) {
    runtimeIsReady = [self prepareJSRuntime];
    retries--;
  }
  if (!runtimeIsReady) {
    [self invalidate];
    NSString *error =
        @"Runtime is not ready for debugging.\n "
         "- Make sure Metro is running.\n"
         "- Make sure the JavaScript Debugger is running and not paused on a "
         "breakpoint or exception and try reloading again.";
    _setupError = RCTErrorWithMessage(error);
    RCTFatal(_setupError);
    return;
  }
}

- (BOOL)connectToProxy
{
  _socketOpenSemaphore = dispatch_semaphore_create(0);
  [_socket open];
  long connected = dispatch_semaphore_wait(_socketOpenSemaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 15));
  return connected == 0 && _socket.readyState == SR_OPEN;
}

- (BOOL)prepareJSRuntime
{
  __block NSError *initError;
  dispatch_semaphore_t s = dispatch_semaphore_create(0);
  [self sendMessage:@{@"method" : @"prepareJSRuntime"}
            onReply:^(NSError *error, NSDictionary<NSString *, id> *reply) {
              initError = error;
              dispatch_semaphore_signal(s);
            }];
  long runtimeIsReady = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10));
  if (initError) {
    RCTLogInfo(@"Websocket runtime setup failed: %@", initError);
  }
  return runtimeIsReady == 0 && initError == nil;
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
  NSError *error = nil;
  NSDictionary<NSString *, id> *reply = RCTJSONParse(message, &error);
  NSNumber *messageID = reply[@"replyID"];
  RCTWSMessageCallback callback = _callbacks[messageID];
  if (callback) {
    callback(error, reply);
    [_callbacks removeObjectForKey:messageID];
  }
}

- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{
  dispatch_semaphore_signal(_socketOpenSemaphore);
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
  dispatch_semaphore_signal(_socketOpenSemaphore);
  RCTLogInfo(@"WebSocket connection failed with error %@", error);
}

- (void)sendMessage:(NSDictionary<NSString *, id> *)message onReply:(RCTWSMessageCallback)callback
{
  static NSUInteger lastID = 10000;

  if (_setupError) {
    callback(_setupError, nil);
    return;
  }

  dispatch_async(_jsQueue, ^{
    if (!self.valid) {
      callback(
          RCTErrorWithMessage(
              @"Runtime is not ready for debugging. Make sure Metro is running and the Remote Debugging browser window is open."),
          nil);
      return;
    }

    NSNumber *expectedID = @(lastID++);
    self->_callbacks[expectedID] = [callback copy];
    NSMutableDictionary<NSString *, id> *messageWithID = [message mutableCopy];
    messageWithID[@"id"] = expectedID;
    [self->_socket sendString:RCTJSONStringify(messageWithID, NULL) error:nil];
  });
}

- (void)executeApplicationScript:(NSData *)script
                       sourceURL:(NSURL *)URL
                      onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
  // Hack: the bridge transitions out of loading state as soon as this method returns, which prevents us
  // from completely invalidating the bridge and preventing an endless barage of RCTLog.logIfNoNativeHook
  // calls if the JS execution environment is broken. We therefore block this thread until this message has returned.
  dispatch_semaphore_t scriptSem = dispatch_semaphore_create(0);

  NSDictionary<NSString *, id> *message = @{
    @"method" : @"executeApplicationScript",
    @"url" : RCTNullIfNil(URL.absoluteString),
    @"inject" : _injectedObjects,
  };
  [self sendMessage:message
            onReply:^(NSError *socketError, NSDictionary<NSString *, id> *reply) {
              if (socketError) {
                onComplete(socketError);
              } else {
                NSString *error = reply[@"error"];
                onComplete(error ? RCTErrorWithMessage(error) : nil);
              }
              dispatch_semaphore_signal(scriptSem);
            }];

  dispatch_semaphore_wait(scriptSem, DISPATCH_TIME_FOREVER);
}

- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
{
  [self _executeJSCall:@"flushedQueue" arguments:@[] callback:onComplete];
}

- (void)callFunctionOnModule:(NSString *)module
                      method:(NSString *)method
                   arguments:(NSArray *)args
                    callback:(RCTJavaScriptCallback)onComplete
{
  [self _executeJSCall:@"callFunctionReturnFlushedQueue" arguments:@[ module, method, args ] callback:onComplete];
}

- (void)invokeCallbackID:(NSNumber *)cbID arguments:(NSArray *)args callback:(RCTJavaScriptCallback)onComplete
{
  [self _executeJSCall:@"invokeCallbackAndReturnFlushedQueue" arguments:@[ cbID, args ] callback:onComplete];
}

- (void)_executeJSCall:(NSString *)method arguments:(NSArray *)arguments callback:(RCTJavaScriptCallback)onComplete
{
  RCTAssert(onComplete != nil, @"callback was missing for exec JS call");
  NSDictionary<NSString *, id> *message = @{@"method" : method, @"arguments" : arguments};
  [self sendMessage:message
            onReply:^(NSError *socketError, NSDictionary<NSString *, id> *reply) {
              if (socketError) {
                onComplete(nil, socketError);
                return;
              }

              NSError *jsonError;
              id result = RCTJSONParse(reply[@"result"], &jsonError);
              NSString *error = reply[@"error"];
              onComplete(result, error ? RCTErrorWithMessage(error) : jsonError);
            }];
}

- (void)injectJSONText:(NSString *)script
    asGlobalObjectNamed:(NSString *)objectName
               callback:(RCTJavaScriptCompleteBlock)onComplete
{
  dispatch_async(_jsQueue, ^{
    self->_injectedObjects[objectName] = script;
    onComplete(nil);
  });
}

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
  RCTExecuteOnMainQueue(block);
}

- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block
{
  dispatch_async(dispatch_get_main_queue(), block);
}

- (void)invalidate
{
  _socket.delegate = nil;
  [_socket closeWithCode:1000 reason:@"Invalidated"];
  _socket = nil;
}

- (BOOL)isValid
{
  return _socket != nil && _socket.readyState == SR_OPEN;
}

- (void)dealloc
{
  RCTAssert(!self.valid, @"-invalidate must be called before -dealloc");
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
  return nullptr;
}

@end

#endif

Class RCTWebSocketExecutorCls(void)
{
#if RCT_DEV
  return RCTWebSocketExecutor.class;
#else
  return nil;
#endif
}

Directory Contents

Dirs: 1 × Files: 57
Name Size Perms Modified Actions
- drwxr-xr-x 2025-03-28 11:04:43
Edit Download
2.08 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.79 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
563 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.40 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
17.27 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
313 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
11.61 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
354 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.34 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
560 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
7.79 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
747 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
4.75 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
273 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
3.97 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
285 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.10 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
305 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
10.95 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
360 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
9.44 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
2.81 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
19.67 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
3.39 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
18.50 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
287 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.66 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
557 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
7.44 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
1.87 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
6.37 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
618 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
3.05 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
418 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
1.31 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
281 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
4.42 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
355 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
3.02 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
665 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
2.39 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
15.07 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
319 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
2.71 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
2.16 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
27.56 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
321 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
988 B lrw-r--r-- 2025-03-28 11:04:43
Edit Download
481 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
6.00 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
1.03 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
12.10 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
448 B lrw-r--r-- 2025-03-28 11:04:41
Edit Download
9.24 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
1.07 KB lrw-r--r-- 2025-03-28 11:04:41
Edit Download
6.25 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
3.01 KB lrw-r--r-- 2025-03-28 11:04:43
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).