From e7680131d7dc841bcdde7f0dbba95bdeedefa126 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 5 May 2017 08:32:15 -0700 Subject: [PATCH] Merge RCTPackagerClient into RCTPackagerConnection Reviewed By: mmmulani Differential Revision: D4988204 fbshipit-source-id: 78e0df5268bfc11e4e0edf8e60494e55022cd9f2 --- Libraries/WebSocket/RCTSRWebSocket.m | 37 +------ React/DevSupport/RCTPackagerClient.h | 15 ++- React/DevSupport/RCTPackagerClient.m | 104 ++++++------------ React/DevSupport/RCTPackagerClientResponder.h | 26 ----- React/DevSupport/RCTPackagerClientResponder.m | 67 ----------- React/DevSupport/RCTPackagerConnection.h | 3 +- React/DevSupport/RCTPackagerConnection.m | 94 +++++++++++++--- React/DevSupport/RCTReloadPackagerMethod.h | 4 +- React/Modules/RCTDevSettings.h | 9 ++ React/Modules/RCTDevSettings.mm | 18 ++- 10 files changed, 152 insertions(+), 225 deletions(-) delete mode 100644 React/DevSupport/RCTPackagerClientResponder.h delete mode 100644 React/DevSupport/RCTPackagerClientResponder.m diff --git a/Libraries/WebSocket/RCTSRWebSocket.m b/Libraries/WebSocket/RCTSRWebSocket.m index 798b3f39b..7febefecf 100644 --- a/Libraries/WebSocket/RCTSRWebSocket.m +++ b/Libraries/WebSocket/RCTSRWebSocket.m @@ -161,33 +161,6 @@ typedef void (^data_callback)(RCTSRWebSocket *webSocket, NSData *data); @interface RCTSRWebSocket () -- (void)_writeData:(NSData *)data; -- (void)_closeWithProtocolError:(NSString *)message; -- (void)_failWithError:(NSError *)error; - -- (void)_disconnect; - -- (void)_readFrameNew; -- (void)_readFrameContinue; - -- (void)_pumpScanner; - -- (void)_pumpWriting; - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; -- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; -- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; -- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; - -- (void)_sendFrameWithOpcode:(RCTSROpCode)opcode data:(id)data; - -- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; -- (void)_RCTSR_commonInit; - -- (void)_initializeStreams; -- (void)_connect; - @property (nonatomic, assign) RCTSRReadyState readyState; @property (nonatomic, strong) NSOperationQueue *delegateOperationQueue; @@ -195,7 +168,6 @@ typedef void (^data_callback)(RCTSRWebSocket *webSocket, NSData *data); @end - @implementation RCTSRWebSocket { NSInteger _webSocketVersion; @@ -255,13 +227,6 @@ typedef void (^data_callback)(RCTSRWebSocket *webSocket, NSData *data); RCTSRIOConsumerPool *_consumerPool; } -static __strong NSData *CRLFCRLF; - -+ (void)initialize; -{ - CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; -} - - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols { RCTAssertParam(request); @@ -434,7 +399,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return; } - if(![self _checkHandshake:_receivedHTTPHeaders]) { + if (![self _checkHandshake:_receivedHTTPHeaders]) { [self _failWithError:[NSError errorWithDomain:RCTSRWebSocketErrorDomain code:2133 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"]}]]; return; } diff --git a/React/DevSupport/RCTPackagerClient.h b/React/DevSupport/RCTPackagerClient.h index 6795fb3be..41cb7b645 100644 --- a/React/DevSupport/RCTPackagerClient.h +++ b/React/DevSupport/RCTPackagerClient.h @@ -8,10 +8,14 @@ */ #import -#import #if RCT_DEV // Only supported in dev mode +@class RCTPackagerClientResponder; +@class RCTSRWebSocket; + +extern const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION; + @protocol RCTPackagerClientMethod - (void)handleRequest:(id)params withResponder:(RCTPackagerClientResponder *)responder; @@ -19,12 +23,11 @@ @end -@interface RCTPackagerClient : NSObject +@interface RCTPackagerClientResponder : NSObject -- (instancetype)initWithURL:(NSURL *)url; -- (void)addHandler:(id)handler forMethod:(NSString *)name; -- (void)start; -- (void)stop; +- (instancetype)initWithId:(id)msgId socket:(RCTSRWebSocket *)socket; +- (void)respondWithResult:(id)result; +- (void)respondWithError:(id)error; @end diff --git a/React/DevSupport/RCTPackagerClient.m b/React/DevSupport/RCTPackagerClient.m index 6117ce6e2..f38617bd0 100644 --- a/React/DevSupport/RCTPackagerClient.m +++ b/React/DevSupport/RCTPackagerClient.m @@ -9,93 +9,57 @@ #import "RCTPackagerClient.h" -#import -#import #import -#import #import #import -#import "RCTPackagerClientResponder.h" - #if RCT_DEV // Only supported in dev mode -@interface RCTPackagerClient () -@end +const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION = 2; -@implementation RCTPackagerClient { - RCTReconnectingWebSocket *_socket; - NSMutableDictionary> *_handlers; +@implementation RCTPackagerClientResponder { + id _msgId; + __weak RCTSRWebSocket *_socket; } -- (instancetype)initWithURL:(NSURL *)url +- (instancetype)initWithId:(id)msgId socket:(RCTSRWebSocket *)socket { if (self = [super init]) { - _socket = [[RCTReconnectingWebSocket alloc] initWithURL:url]; - _socket.delegate = self; - _handlers = [NSMutableDictionary new]; + _msgId = msgId; + _socket = socket; } return self; } -- (void)addHandler:(id)handler forMethod:(NSString *)name +- (void)respondWithResult:(id)result { - _handlers[name] = handler; -} - -- (void)start -{ - _socket.delegate = self; - [_socket start]; -} - -- (void)stop -{ - [_socket stop]; -} - -- (BOOL)isSupportedVersion:(NSNumber *)version -{ - NSArray *const kSupportedVersions = @[ @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION) ]; - return [kSupportedVersions containsObject:version]; -} - -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message -{ - if (!_handlers) { - return; - } - - NSError *error = nil; - NSDictionary *msg = RCTJSONParse(message, &error); - - if (error) { - RCTLogError(@"%@ failed to parse message with error %@\n\n%@\n", [self class], error, msg); - return; - } - - if (![self isSupportedVersion:msg[@"version"]]) { - RCTLogError(@"%@ received message with not supported version %@", [self class], msg[@"version"]); - return; - } - - id methodHandler = _handlers[msg[@"method"]]; - if (!methodHandler) { - if (msg[@"id"]) { - NSString *errorMsg = [NSString stringWithFormat:@"%@ no handler found for method %@", [self class], msg[@"method"]]; - RCTLogError(errorMsg, msg[@"method"]); - [[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"] - socket:webSocket] respondWithError:errorMsg]; - } - return; // If it was a broadcast then we ignore it gracefully - } - - if (msg[@"id"]) { - [methodHandler handleRequest:msg[@"params"] - withResponder:[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"] - socket:webSocket]]; + NSDictionary *msg = @{ + @"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION), + @"id": _msgId, + @"result": result, + }; + NSError *jsError = nil; + NSString *message = RCTJSONStringify(msg, &jsError); + if (jsError) { + RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError); } else { - [methodHandler handleNotification:msg[@"params"]]; + [_socket send:message]; + } +} + +- (void)respondWithError:(id)error +{ + NSDictionary *msg = @{ + @"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION), + @"id": _msgId, + @"error": error, + }; + NSError *jsError = nil; + NSString *message = RCTJSONStringify(msg, &jsError); + if (jsError) { + RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError); + } else { + [_socket send:message]; } } @end diff --git a/React/DevSupport/RCTPackagerClientResponder.h b/React/DevSupport/RCTPackagerClientResponder.h deleted file mode 100644 index 0d15ac1cf..000000000 --- a/React/DevSupport/RCTPackagerClientResponder.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#if RCT_DEV // Only supported in dev mode - -extern const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION; - -@class RCTSRWebSocket; - -@interface RCTPackagerClientResponder : NSObject - -- (instancetype)initWithId:(id)msgId socket:(RCTSRWebSocket *)socket; -- (void)respondWithResult:(id)result; -- (void)respondWithError:(id)error; - -@end - -#endif diff --git a/React/DevSupport/RCTPackagerClientResponder.m b/React/DevSupport/RCTPackagerClientResponder.m deleted file mode 100644 index 896b009e5..000000000 --- a/React/DevSupport/RCTPackagerClientResponder.m +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "RCTPackagerClientResponder.h" - -#import -#import -#import - -#if RCT_DEV // Only supported in dev mode - -const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION = 2; - -@implementation RCTPackagerClientResponder { - id _msgId; - __weak RCTSRWebSocket *_socket; -} - -- (instancetype)initWithId:(id)msgId socket:(RCTSRWebSocket *)socket -{ - if (self = [super init]) { - _msgId = msgId; - _socket = socket; - } - return self; -} - -- (void)respondWithResult:(id)result -{ - NSDictionary *msg = @{ - @"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION), - @"id": _msgId, - @"result": result, - }; - NSError *jsError = nil; - NSString *message = RCTJSONStringify(msg, &jsError); - if (jsError) { - RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError); - } else { - [_socket send:message]; - } -} - -- (void)respondWithError:(id)error -{ - NSDictionary *msg = @{ - @"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION), - @"id": _msgId, - @"error": error, - }; - NSError *jsError = nil; - NSString *message = RCTJSONStringify(msg, &jsError); - if (jsError) { - RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError); - } else { - [_socket send:message]; - } -} -@end - -#endif diff --git a/React/DevSupport/RCTPackagerConnection.h b/React/DevSupport/RCTPackagerConnection.h index d93dbfd0f..2e115f9a2 100644 --- a/React/DevSupport/RCTPackagerConnection.h +++ b/React/DevSupport/RCTPackagerConnection.h @@ -14,6 +14,7 @@ #if RCT_DEV @class RCTBridge; +@protocol RCTPackagerClientMethod; /** * Encapsulates connection to React Native packager @@ -21,7 +22,7 @@ @interface RCTPackagerConnection : NSObject - (instancetype)initWithBridge:(RCTBridge *)bridge; -- (void)connect; +- (void)addHandler:(id)handler forMethod:(NSString *)name; @end diff --git a/React/DevSupport/RCTPackagerConnection.m b/React/DevSupport/RCTPackagerConnection.m index a063bb3c1..6ced2f35a 100644 --- a/React/DevSupport/RCTPackagerConnection.m +++ b/React/DevSupport/RCTPackagerConnection.m @@ -13,22 +13,38 @@ #import #import +#import +#import +#import +#import +#import +#import #import -#import "RCTPackagerClient.h" #import "RCTReloadPackagerMethod.h" #import "RCTSamplingProfilerPackagerMethod.h" #if RCT_DEV +@interface RCTPackagerConnection () +@end + @implementation RCTPackagerConnection { RCTBridge *_bridge; + RCTReconnectingWebSocket *_socket; + NSMutableDictionary> *_handlers; } - (instancetype)initWithBridge:(RCTBridge *)bridge { if (self = [super init]) { _bridge = bridge; + + _handlers = [NSMutableDictionary new]; + _handlers[@"reload"] = [[RCTReloadPackagerMethod alloc] initWithBridge:_bridge]; + _handlers[@"pokeSamplingProfiler"] = [[RCTSamplingProfilerPackagerMethod alloc] initWithBridge:_bridge]; + + [self connect]; } return self; } @@ -45,25 +61,20 @@ // The jsPackagerClient is a static map that holds different packager clients per the packagerURL // In case many instances of DevMenu are created, the latest instance that use the same URL as // previous instances will override given packager client's method handlers - static NSMutableDictionary *jsPackagerClients = nil; - if (jsPackagerClients == nil) { - jsPackagerClients = [NSMutableDictionary new]; + static NSMutableDictionary *socketConnections = nil; + if (socketConnections == nil) { + socketConnections = [NSMutableDictionary new]; } NSString *key = [url absoluteString]; - RCTPackagerClient *packagerClient = jsPackagerClients[key]; - if (!packagerClient) { - packagerClient = [[RCTPackagerClient alloc] initWithURL:url]; - jsPackagerClients[key] = packagerClient; - } else { - [packagerClient stop]; + RCTReconnectingWebSocket *webSocket = socketConnections[key]; + if (!webSocket) { + webSocket = [[RCTReconnectingWebSocket alloc] initWithURL:url]; + [webSocket start]; + socketConnections[key] = webSocket; } - [packagerClient addHandler:[[RCTReloadPackagerMethod alloc] initWithBridge:_bridge] - forMethod:@"reload"]; - [packagerClient addHandler:[[RCTSamplingProfilerPackagerMethod alloc] initWithBridge:_bridge] - forMethod:@"pokeSamplingProfiler"]; - [packagerClient start]; + webSocket.delegate = self; } - (NSURL *)packagerURL @@ -82,6 +93,59 @@ return [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@:%@/message?role=ios-rn-rctdevmenu", scheme, host, port]]; } + +- (void)addHandler:(id)handler forMethod:(NSString *)name +{ + _handlers[name] = handler; +} + +static BOOL isSupportedVersion(NSNumber *version) +{ + NSArray *const kSupportedVersions = @[ @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION) ]; + return [kSupportedVersions containsObject:version]; +} + +#pragma mark - RCTWebSocketProtocolDelegate + +- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message +{ + if (!_handlers) { + return; + } + + NSError *error = nil; + NSDictionary *msg = RCTJSONParse(message, &error); + + if (error) { + RCTLogError(@"%@ failed to parse message with error %@\n\n%@\n", [self class], error, msg); + return; + } + + if (!isSupportedVersion(msg[@"version"])) { + RCTLogError(@"%@ received message with not supported version %@", [self class], msg[@"version"]); + return; + } + + id methodHandler = _handlers[msg[@"method"]]; + if (!methodHandler) { + if (msg[@"id"]) { + NSString *errorMsg = [NSString stringWithFormat:@"%@ no handler found for method %@", [self class], msg[@"method"]]; + RCTLogError(errorMsg, msg[@"method"]); + [[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"] + socket:webSocket] respondWithError:errorMsg]; + } + return; // If it was a broadcast then we ignore it gracefully + } + + if (msg[@"id"]) { + [methodHandler handleRequest:msg[@"params"] + withResponder:[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"] + socket:webSocket]]; + } else { + [methodHandler handleNotification:msg[@"params"]]; + } +} + @end #endif diff --git a/React/DevSupport/RCTReloadPackagerMethod.h b/React/DevSupport/RCTReloadPackagerMethod.h index 6441cbf27..cd157edc6 100644 --- a/React/DevSupport/RCTReloadPackagerMethod.h +++ b/React/DevSupport/RCTReloadPackagerMethod.h @@ -9,10 +9,10 @@ #import -#if RCT_DEV // Only supported in dev mode - @class RCTBridge; +#if RCT_DEV // Only supported in dev mode + @interface RCTReloadPackagerMethod : NSObject - (instancetype)initWithBridge:(RCTBridge *)bridge; diff --git a/React/Modules/RCTDevSettings.h b/React/Modules/RCTDevSettings.h index cfecfa5d5..d4b5abe6a 100644 --- a/React/Modules/RCTDevSettings.h +++ b/React/Modules/RCTDevSettings.h @@ -8,6 +8,9 @@ */ #import +#import + +@protocol RCTPackagerClientMethod; /** * An abstraction for a key-value store to manage RCTDevSettings behavior. @@ -94,6 +97,12 @@ */ @property (nonatomic, assign) BOOL isJSCProfilingEnabled; +#if RCT_DEV + +- (void)addHandler:(id)handler forPackagerMethod:(NSString *)name; + +#endif + @end @interface RCTBridge (RCTDevSettings) diff --git a/React/Modules/RCTDevSettings.mm b/React/Modules/RCTDevSettings.mm index 037b0d629..69d47d0c7 100644 --- a/React/Modules/RCTDevSettings.mm +++ b/React/Modules/RCTDevSettings.mm @@ -141,6 +141,8 @@ RCT_EXPORT_MODULE() { if (self = [super init]) { _dataSource = dataSource; + [self _configurePackagerConnection]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jsLoaded:) name:RCTJavaScriptDidLoadNotification @@ -149,7 +151,6 @@ RCT_EXPORT_MODULE() // Delay setup until after Bridge init dispatch_async(dispatch_get_main_queue(), ^{ [self _synchronizeAllSettings]; - [self _configurePackagerConnection]; }); } return self; @@ -385,6 +386,20 @@ RCT_EXPORT_METHOD(toggleElementInspector) } } +#if ENABLE_PACKAGER_CONNECTION + +- (void)addHandler:(id)handler forPackagerMethod:(NSString *)name +{ + RCTAssert(_packagerConnection, @"Expected packager connection"); + [_packagerConnection addHandler:handler forMethod:name]; +} + +#elif RCT_DEV + +- (void)addHandler:(id)handler forPackagerMethod:(NSString *)name {} + +#endif + #pragma mark - Internal - (void)_configurePackagerConnection @@ -395,7 +410,6 @@ RCT_EXPORT_METHOD(toggleElementInspector) } _packagerConnection = [[RCTPackagerConnection alloc] initWithBridge:_bridge]; - [_packagerConnection connect]; #endif }