123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- * Copyright (c) Facebook, Inc. and its 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/RCTPackagerConnection.h>
- #import <objc/runtime.h>
- #import <algorithm>
- #import <vector>
- #import <React/RCTAssert.h>
- #import <React/RCTBridge.h>
- #import <React/RCTBundleURLProvider.h>
- #import <React/RCTConvert.h>
- #import <React/RCTDefines.h>
- #import <React/RCTLog.h>
- #import <React/RCTPackagerClient.h>
- #import <React/RCTReconnectingWebSocket.h>
- #import <React/RCTUtils.h>
- #if RCT_DEV
- #import <React/RCTSRWebSocket.h>
- @interface RCTPackagerConnection () <RCTReconnectingWebSocketDelegate>
- @end
- template <typename Handler>
- struct Registration {
- NSString *method;
- Handler handler;
- dispatch_queue_t queue;
- uint32_t token;
- };
- @implementation RCTPackagerConnection {
- std::mutex _mutex; // protects all ivars
- RCTReconnectingWebSocket *_socket;
- BOOL _socketConnected;
- NSString *_serverHostForSocket;
- id _bundleURLChangeObserver;
- uint32_t _nextToken;
- std::vector<Registration<RCTNotificationHandler>> _notificationRegistrations;
- std::vector<Registration<RCTRequestHandler>> _requestRegistrations;
- std::vector<Registration<RCTConnectedHandler>> _connectedRegistrations;
- }
- + (instancetype)sharedPackagerConnection
- {
- static RCTPackagerConnection *connection;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- connection = [RCTPackagerConnection new];
- });
- return connection;
- }
- - (instancetype)init
- {
- if (self = [super init]) {
- _nextToken = 1; // Prevent randomly erasing a handler if you pass a bogus 0 token
- _serverHostForSocket = [[RCTBundleURLProvider sharedSettings] packagerServerHost];
- _socket = socketForLocation(_serverHostForSocket);
- _socket.delegate = self;
- [_socket start];
- RCTPackagerConnection *const __weak weakSelf = self;
- _bundleURLChangeObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:RCTBundleURLProviderUpdatedNotification
- object:nil
- queue:[NSOperationQueue mainQueue]
- usingBlock:^(NSNotification *_Nonnull __unused note) {
- [weakSelf bundleURLSettingsChanged];
- }];
- }
- return self;
- }
- static RCTReconnectingWebSocket *socketForLocation(NSString *const serverHost)
- {
- NSURLComponents *const components = [NSURLComponents new];
- components.host = serverHost ?: @"localhost";
- components.scheme = @"http";
- components.port = @(kRCTBundleURLProviderDefaultPort);
- components.path = @"/message";
- components.queryItems = @[ [NSURLQueryItem queryItemWithName:@"role" value:@"ios"] ];
- static dispatch_queue_t queue;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- queue = dispatch_queue_create("com.facebook.RCTPackagerConnectionQueue", DISPATCH_QUEUE_SERIAL);
- });
- return [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue];
- }
- - (void)stop
- {
- std::lock_guard<std::mutex> l(_mutex);
- if (_socket == nil) {
- // Already stopped
- return;
- }
- [[NSNotificationCenter defaultCenter] removeObserver:_bundleURLChangeObserver];
- _bundleURLChangeObserver = nil;
- _socketConnected = NO;
- [_socket stop];
- _socket = nil;
- _notificationRegistrations.clear();
- _requestRegistrations.clear();
- }
- - (void)bundleURLSettingsChanged
- {
- std::lock_guard<std::mutex> l(_mutex);
- if (_socket == nil) {
- return; // already stopped
- }
- NSString *const serverHost = [[RCTBundleURLProvider sharedSettings] packagerServerHost];
- if ([serverHost isEqual:_serverHostForSocket]) {
- return; // unchanged
- }
- _socket.delegate = nil;
- [_socket stop];
- _serverHostForSocket = serverHost;
- _socket = socketForLocation(serverHost);
- _socket.delegate = self;
- [_socket start];
- }
- - (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler
- queue:(dispatch_queue_t)queue
- forMethod:(NSString *)method
- {
- std::lock_guard<std::mutex> l(_mutex);
- const auto token = _nextToken++;
- _notificationRegistrations.push_back({method, handler, queue, token});
- return token;
- }
- - (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler
- queue:(dispatch_queue_t)queue
- forMethod:(NSString *)method
- {
- std::lock_guard<std::mutex> l(_mutex);
- const auto token = _nextToken++;
- _requestRegistrations.push_back({method, handler, queue, token});
- return token;
- }
- - (RCTHandlerToken)addConnectedHandler:(RCTConnectedHandler)handler queue:(dispatch_queue_t)queue
- {
- std::lock_guard<std::mutex> l(_mutex);
- if (_socketConnected) {
- dispatch_async(queue, ^{
- handler();
- });
- return 0; // _nextToken starts at 1, so 0 is a no-op token
- } else {
- const auto token = _nextToken++;
- _connectedRegistrations.push_back({nil, handler, queue, token});
- return token;
- }
- }
- - (void)removeHandler:(RCTHandlerToken)token
- {
- std::lock_guard<std::mutex> l(_mutex);
- eraseRegistrationsWithToken(_notificationRegistrations, token);
- eraseRegistrationsWithToken(_requestRegistrations, token);
- eraseRegistrationsWithToken(_connectedRegistrations, token);
- }
- template <typename Handler>
- static void eraseRegistrationsWithToken(std::vector<Registration<Handler>> ®istrations, RCTHandlerToken token)
- {
- registrations.erase(
- std::remove_if(
- registrations.begin(), registrations.end(), [&token](const auto ®) { return reg.token == token; }),
- registrations.end());
- }
- - (void)addHandler:(id<RCTPackagerClientMethod>)handler forMethod:(NSString *)method
- {
- dispatch_queue_t queue =
- [handler respondsToSelector:@selector(methodQueue)] ? [handler methodQueue] : dispatch_get_main_queue();
- [self
- addNotificationHandler:^(NSDictionary<NSString *, id> *notification) {
- [handler handleNotification:notification];
- }
- queue:queue
- forMethod:method];
- [self
- addRequestHandler:^(NSDictionary<NSString *, id> *request, RCTPackagerClientResponder *responder) {
- [handler handleRequest:request withResponder:responder];
- }
- queue:queue
- forMethod:method];
- }
- static BOOL isSupportedVersion(NSNumber *version)
- {
- NSArray<NSNumber *> *const kSupportedVersions = @[ @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION) ];
- return [kSupportedVersions containsObject:version];
- }
- #pragma mark - RCTReconnectingWebSocketDelegate
- - (void)reconnectingWebSocketDidOpen:(__unused RCTReconnectingWebSocket *)webSocket
- {
- std::vector<Registration<RCTConnectedHandler>> registrations;
- {
- std::lock_guard<std::mutex> l(_mutex);
- _socketConnected = YES;
- registrations = _connectedRegistrations;
- _connectedRegistrations.clear();
- }
- for (const auto ®istration : registrations) {
- // Beware: don't capture the reference to handler in a dispatched block!
- RCTConnectedHandler handler = registration.handler;
- dispatch_async(registration.queue, ^{
- handler();
- });
- }
- }
- - (void)reconnectingWebSocket:(RCTReconnectingWebSocket *)webSocket didReceiveMessage:(id)message
- {
- NSError *error = nil;
- NSDictionary<NSString *, id> *msg = RCTJSONParse(message, &error);
- if (error) {
- RCTLogError(@"%@ failed to parse message with error %@\n<message>\n%@\n</message>", [self class], error, msg);
- return;
- }
- if (!isSupportedVersion(msg[@"version"])) {
- RCTLogError(@"%@ received message with not supported version %@", [self class], msg[@"version"]);
- return;
- }
- NSString *const method = msg[@"method"];
- NSDictionary<NSString *, id> *const params = msg[@"params"];
- id messageId = msg[@"id"];
- if (messageId) { // Request
- const std::vector<Registration<RCTRequestHandler>> registrations(
- registrationsWithMethod(_mutex, _requestRegistrations, method));
- if (registrations.empty()) {
- RCTLogError(@"No handler found for packager method %@", msg[@"method"]);
- [[[RCTPackagerClientResponder alloc] initWithId:messageId socket:webSocket]
- respondWithError:[NSString stringWithFormat:@"No handler found for packager method %@", msg[@"method"]]];
- } else {
- // If there are multiple matching request registrations, only one can win;
- // otherwise the packager would get multiple responses. Choose the last one.
- RCTRequestHandler handler = registrations.back().handler;
- dispatch_async(registrations.back().queue, ^{
- handler(params, [[RCTPackagerClientResponder alloc] initWithId:messageId socket:webSocket]);
- });
- }
- } else { // Notification
- const std::vector<Registration<RCTNotificationHandler>> registrations(
- registrationsWithMethod(_mutex, _notificationRegistrations, method));
- for (const auto ®istration : registrations) {
- // Beware: don't capture the reference to handler in a dispatched block!
- RCTNotificationHandler handler = registration.handler;
- dispatch_async(registration.queue, ^{
- handler(params);
- });
- }
- }
- }
- - (void)reconnectingWebSocketDidClose:(__unused RCTReconnectingWebSocket *)webSocket
- {
- std::lock_guard<std::mutex> l(_mutex);
- _socketConnected = NO;
- }
- template <typename Handler>
- static std::vector<Registration<Handler>>
- registrationsWithMethod(std::mutex &mutex, const std::vector<Registration<Handler>> ®istrations, NSString *method)
- {
- std::lock_guard<std::mutex> l(mutex); // Scope lock acquisition to prevent deadlock when calling out
- std::vector<Registration<Handler>> matches;
- for (const auto ® : registrations) {
- if ([reg.method isEqual:method]) {
- matches.push_back(reg);
- }
- }
- return matches;
- }
- @end
- #endif
|