1// Copyright 2015-present 650 Industries. All rights reserved.
2
3/**
4 * This file is compiled without ARC - the memory needs to be managed by hand.
5 */
6
7#import <EXDevLauncher/RCTPackagerConnection+EXDevLauncherPackagerConnectionInterceptor.h>
8
9#import <React/RCTReconnectingWebSocket.h>
10
11#import <objc/runtime.h>
12#import <mutex>
13
14#if RCT_DEV
15
16static RCTReconnectingWebSocket *createSocketForURL(NSURL * url)
17{
18  NSURLComponents *const components = [NSURLComponents new];
19  components.host = [url host];
20  components.scheme = @"http";
21  components.port =  [url port];
22  components.path = @"/message";
23  components.queryItems = @[[NSURLQueryItem queryItemWithName:@"role" value:@"ios"]];
24  static dispatch_queue_t queue;
25  static dispatch_once_t onceToken;
26  dispatch_once(&onceToken, ^{
27    queue = dispatch_queue_create("com.facebook.RCTPackagerConnectionQueue", DISPATCH_QUEUE_SERIAL);
28  });
29
30  RCTReconnectingWebSocket *webSocket = [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue];
31  [components release];
32  return [webSocket autorelease]; // adds to ARC
33}
34
35@implementation RCTPackagerConnection (EXDevLauncherPackagerConnectionInterceptor)
36
37/**
38 * Sets the WebSocket URL.
39 */
40- (void)setSocketConnectionURL:(NSURL *)url
41{
42  // Mutex isn't an objective-c object - [self valueForKey:@"_mutex"] will fail.
43  // The other solution requires `object_getInstanceVariable` which doesn't work with ARC.
44  // That's the only solution to get private variables that comes from cpp.
45  Ivar mutexVar = object_getInstanceVariable(self, "_mutex", NULL);
46  ptrdiff_t mutexOffset = ivar_getOffset(mutexVar);
47  unsigned char* selfPtr = (unsigned char *)(__bridge void*)self;
48  std::mutex *mutex = (std::mutex *)(selfPtr + mutexOffset);
49
50  RCTReconnectingWebSocket *oldSocket = (RCTReconnectingWebSocket *)[self valueForKey:@"_socket"];
51
52  std::lock_guard<std::mutex> l(*mutex);
53  if (oldSocket == nil) {
54    return; // already stopped
55  }
56
57  oldSocket.delegate = nil;
58  [oldSocket stop];
59
60  RCTReconnectingWebSocket *newSocket = createSocketForURL(url);
61  newSocket.delegate = (id<RCTReconnectingWebSocketDelegate>)self;
62  [newSocket start];
63
64  @try {
65    // _serverHostForSocket was removed in RN 0.64
66    [self setValue:url.absoluteString forKey:@"_serverHostForSocket"];
67  } @catch(id exception) {}
68  [self setValue:newSocket forKey:@"_socket"];
69}
70
71@end
72
73#endif
74