diff --git a/BUCK b/BUCK index 593f7ec2a5d5a9..cc2d7f7f211ce4 100644 --- a/BUCK +++ b/BUCK @@ -320,7 +320,6 @@ REACT_PUBLIC_HEADERS = { "React/RCTRootShadowView.h": RCTVIEWS_PATH + "RCTRootShadowView.h", "React/RCTRootView.h": RCTBASE_PATH + "RCTRootView.h", "React/RCTRootViewDelegate.h": RCTBASE_PATH + "RCTRootViewDelegate.h", - "React/RCTSRWebSocket.h": RCTLIB_PATH + "WebSocket/RCTSRWebSocket.h", "React/RCTScrollEvent.h": RCTVIEWS_PATH + "ScrollView/RCTScrollEvent.h", "React/RCTScrollView.h": RCTVIEWS_PATH + "ScrollView/RCTScrollView.h", "React/RCTScrollableProtocol.h": RCTVIEWS_PATH + "ScrollView/RCTScrollableProtocol.h", @@ -454,6 +453,7 @@ rn_apple_xplat_cxx_library( ], deps = [ YOGA_CXX_TARGET, + "//fbobjc/VendorLib/SocketRocket:SocketRocket", react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("reactperflogger:reactperflogger"), ], diff --git a/Libraries/WebSocket/RCTReconnectingWebSocket.m b/Libraries/WebSocket/RCTReconnectingWebSocket.m index d557815eb1d2fc..1bd4de74a72488 100644 --- a/Libraries/WebSocket/RCTReconnectingWebSocket.m +++ b/Libraries/WebSocket/RCTReconnectingWebSocket.m @@ -10,16 +10,16 @@ #import #import -#import +#import #if RCT_DEV // Only supported in dev mode -@interface RCTReconnectingWebSocket () +@interface RCTReconnectingWebSocket () @end @implementation RCTReconnectingWebSocket { NSURL *_url; - RCTSRWebSocket *_socket; + SRWebSocket *_socket; BOOL _stopped; } @@ -39,14 +39,14 @@ - (instancetype)initWithURL:(NSURL *)url - (void)send:(id)data { - [_socket send:data]; + [_socket sendData:data error:nil]; } - (void)start { [self stop]; _stopped = NO; - _socket = [[RCTSRWebSocket alloc] initWithURL:_url]; + _socket = [[SRWebSocket alloc] initWithURL:_url]; _socket.delegate = self; [_socket setDelegateDispatchQueue:_delegateDispatchQueue]; [_socket open]; @@ -60,7 +60,7 @@ - (void)stop _socket = nil; } -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { [_delegate reconnectingWebSocket:self didReceiveMessage:message]; } @@ -71,7 +71,7 @@ - (void)reconnect return; } - __weak RCTSRWebSocket *socket = _socket; + __weak SRWebSocket *socket = _socket; __weak __typeof(self) weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @@ -82,12 +82,12 @@ - (void)reconnect }); } -- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket +- (void)webSocketDidOpen:(SRWebSocket *)webSocket { [_delegate reconnectingWebSocketDidOpen:self]; } -- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { [_delegate reconnectingWebSocketDidClose:self]; if ([error code] != ECONNREFUSED) { @@ -95,7 +95,7 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error } } -- (void)webSocket:(RCTSRWebSocket *)webSocket +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean diff --git a/Libraries/WebSocket/RCTSRWebSocket.h b/Libraries/WebSocket/RCTSRWebSocket.h deleted file mode 100644 index 4480121b465f1c..00000000000000 --- a/Libraries/WebSocket/RCTSRWebSocket.h +++ /dev/null @@ -1,136 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -typedef NS_ENUM(unsigned int, RCTSRReadyState) { - RCTSR_CONNECTING = 0, - RCTSR_OPEN = 1, - RCTSR_CLOSING = 2, - RCTSR_CLOSED = 3, -}; - -typedef NS_ENUM(NSInteger, RCTSRStatusCode) { - RCTSRStatusCodeNormal = 1000, - RCTSRStatusCodeGoingAway = 1001, - RCTSRStatusCodeProtocolError = 1002, - RCTSRStatusCodeUnhandledType = 1003, - // 1004 reserved. - RCTSRStatusNoStatusReceived = 1005, - // 1004-1006 reserved. - RCTSRStatusCodeInvalidUTF8 = 1007, - RCTSRStatusCodePolicyViolated = 1008, - RCTSRStatusCodeMessageTooBig = 1009, -}; - -@class RCTSRWebSocket; - -extern NSString *const RCTSRWebSocketErrorDomain; -extern NSString *const RCTSRHTTPResponseErrorKey; - -#pragma mark - RCTSRWebSocketDelegate - -@protocol RCTSRWebSocketDelegate; - -#pragma mark - RCTSRWebSocket - -@interface RCTSRWebSocket : NSObject - -@property (nonatomic, weak) id delegate; - -@property (nonatomic, readonly) RCTSRReadyState readyState; -@property (nonatomic, readonly, strong) NSURL *url; - -// This returns the negotiated protocol. -// It will be nil until after the handshake completes. -@property (nonatomic, readonly, copy) NSString *protocol; - -// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. -- (instancetype)initWithURLRequest:(NSURLRequest *)request - protocols:(NSArray *)protocols NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithURLRequest:(NSURLRequest *)request; - -// Some helper constructors. -- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; -- (instancetype)initWithURL:(NSURL *)url; - -// Delegate queue will be dispatch_main_queue by default. -// You cannot set both OperationQueue and dispatch_queue. -- (void)setDelegateOperationQueue:(NSOperationQueue *)queue; -- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; - -// By default, it will schedule itself on +[NSRunLoop RCTSR_networkRunLoop] using defaultModes. -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; -- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; - -// RCTSRWebSockets are intended for one-time-use only. Open should be called once and only once. -- (void)open; - -- (void)close; -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; - -// Send a UTF8 String or Data. -- (void)send:(id)data; - -// Send Data (can be nil) in a ping message. -- (void)sendPing:(NSData *)data; - -@end - -#pragma mark - RCTSRWebSocketDelegate - -@protocol RCTSRWebSocketDelegate - -// message will either be an NSString if the server is using text -// or NSData if the server is using binary. -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message; - -@optional - -- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket; -- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error; -- (void)webSocket:(RCTSRWebSocket *)webSocket - didCloseWithCode:(NSInteger)code - reason:(NSString *)reason - wasClean:(BOOL)wasClean; -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; - -@end - -#pragma mark - NSURLRequest (CertificateAdditions) - -@interface NSURLRequest (CertificateAdditions) - -@property (nonatomic, readonly, copy) NSArray *RCTSR_SSLPinnedCertificates; - -@end - -#pragma mark - NSMutableURLRequest (CertificateAdditions) - -@interface NSMutableURLRequest (CertificateAdditions) - -@property (nonatomic, copy) NSArray *RCTSR_SSLPinnedCertificates; - -@end - -#pragma mark - NSRunLoop (RCTSRWebSocket) - -@interface NSRunLoop (RCTSRWebSocket) - -+ (NSRunLoop *)RCTSR_networkRunLoop; - -@end diff --git a/Libraries/WebSocket/RCTSRWebSocket.m b/Libraries/WebSocket/RCTSRWebSocket.m deleted file mode 100644 index 925596f8677f2c..00000000000000 --- a/Libraries/WebSocket/RCTSRWebSocket.m +++ /dev/null @@ -1,1746 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import - -#import - -#import -#import -#import - -typedef NS_ENUM(NSInteger, RCTSROpCode) { - RCTSROpCodeTextFrame = 0x1, - RCTSROpCodeBinaryFrame = 0x2, - // 3-7 reserved. - RCTSROpCodeConnectionClose = 0x8, - RCTSROpCodePing = 0x9, - RCTSROpCodePong = 0xA, - // B-F reserved. -}; - -typedef struct { - BOOL fin; - // BOOL rsv1; - // BOOL rsv2; - // BOOL rsv3; - uint8_t opcode; - BOOL masked; - uint64_t payload_length; -} frame_header; - -static NSString *const RCTSRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -//#define RCTSR_ENABLE_LOG -#ifdef RCTSR_ENABLE_LOG -#define RCTSRLog(format...) RCTLogInfo(format) -#else -#define RCTSRLog(...) \ - do { \ - } while (0) -#endif - -// This is a hack, and probably not optimal -static inline int32_t validate_dispatch_data_partial_string(NSData *data) -{ - static const int maxCodepointSize = 3; - - for (int i = 0; i < maxCodepointSize; i++) { - NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes - length:data.length - i - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - if (str) { - return (int32_t)data.length - i; - } - } - - return -1; -} - -@interface NSData (RCTSRWebSocket) - -@property (nonatomic, readonly, copy) NSString *stringBySHA1ThenBase64Encoding; - -@end - -@interface NSString (RCTSRWebSocket) - -@property (nonatomic, readonly, copy) NSString *stringBySHA1ThenBase64Encoding; - -@end - -@interface NSURL (RCTSRWebSocket) - -// The origin isn't really applicable for a native application. -// So instead, just map ws -> http and wss -> https. -@property (nonatomic, readonly, copy) NSString *RCTSR_origin; - -@end - -@interface _RCTSRRunLoopThread : NSThread - -@property (nonatomic, readonly) NSRunLoop *runLoop; - -@end - -static NSString *newSHA1String(const char *bytes, size_t length) -{ - uint8_t md[CC_SHA1_DIGEST_LENGTH]; - - assert(length >= 0); - assert(length <= UINT32_MAX); - CC_SHA1(bytes, (CC_LONG)length, md); - - NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; - return [data base64EncodedStringWithOptions:0]; -} - -@implementation NSData (RCTSRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding -{ - return newSHA1String(self.bytes, self.length); -} - -@end - -@implementation NSString (RCTSRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding -{ - return newSHA1String(self.UTF8String, self.length); -} - -@end - -NSString *const RCTSRWebSocketErrorDomain = @"RCTSRWebSocketErrorDomain"; -NSString *const RCTSRHTTPResponseErrorKey = @"HTTPResponseStatusCode"; - -// Returns number of bytes consumed. Returning 0 means you didn't match. -// Sends bytes to callback handler; -typedef size_t (^stream_scanner)(NSData *collected_data); - -typedef void (^data_callback)(RCTSRWebSocket *webSocket, NSData *data); - -@interface RCTSRIOConsumer : NSObject - -@property (nonatomic, copy, readonly) stream_scanner consumer; -@property (nonatomic, copy, readonly) data_callback handler; -@property (nonatomic, assign) size_t bytesNeeded; -@property (nonatomic, assign, readonly) BOOL readToCurrentFrame; -@property (nonatomic, assign, readonly) BOOL unmaskBytes; - -@end - -// This class is not thread-safe, and is expected to always be run on the same queue. -@interface RCTSRIOConsumerPool : NSObject - -- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize NS_DESIGNATED_INITIALIZER; - -- (RCTSRIOConsumer *)consumerWithScanner:(stream_scanner)scanner - handler:(data_callback)handler - bytesNeeded:(size_t)bytesNeeded - readToCurrentFrame:(BOOL)readToCurrentFrame - unmaskBytes:(BOOL)unmaskBytes; -- (void)returnConsumer:(RCTSRIOConsumer *)consumer; - -@end - -@interface RCTSRWebSocket () - -@property (nonatomic, assign) RCTSRReadyState readyState; - -@property (nonatomic, strong) NSOperationQueue *delegateOperationQueue; -@property (nonatomic, strong) dispatch_queue_t delegateDispatchQueue; - -@end - -@implementation RCTSRWebSocket { - NSInteger _webSocketVersion; - - NSOperationQueue *_delegateOperationQueue; - dispatch_queue_t _delegateDispatchQueue; - - dispatch_queue_t _workQueue; - NSMutableArray *_consumers; - - NSInputStream *_inputStream; - NSOutputStream *_outputStream; - - NSMutableData *_readBuffer; - NSUInteger _readBufferOffset; - - NSMutableData *_outputBuffer; - NSUInteger _outputBufferOffset; - - uint8_t _currentFrameOpcode; - size_t _currentFrameCount; - size_t _readOpCount; - uint32_t _currentStringScanPosition; - NSMutableData *_currentFrameData; - - NSString *_closeReason; - - NSString *_secKey; - - BOOL _pinnedCertFound; - - uint8_t _currentReadMaskKey[4]; - size_t _currentReadMaskOffset; - - BOOL _consumerStopped; - - BOOL _closeWhenFinishedWriting; - BOOL _failed; - - BOOL _secure; - NSURLRequest *_urlRequest; - - CFHTTPMessageRef _receivedHTTPHeaders; - - BOOL _sentClose; - BOOL _didFail; - int _closeCode; - - BOOL _isPumping; - - BOOL _cleanupScheduled; - - NSMutableSet *_scheduledRunloops; - - // We use this to retain ourselves. - __strong RCTSRWebSocket *_selfRetain; - - NSArray *_requestedProtocols; - RCTSRIOConsumerPool *_consumerPool; -} - -- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols -{ - RCTAssertParam(request); - - if ((self = [super init])) { - _url = request.URL; - _urlRequest = request; - - _requestedProtocols = [protocols copy]; - - [self _RCTSR_commonInit]; - } - return self; -} - -RCT_NOT_IMPLEMENTED(-(instancetype)init) - -- (instancetype)initWithURLRequest:(NSURLRequest *)request -{ - return [self initWithURLRequest:request protocols:nil]; -} - -- (instancetype)initWithURL:(NSURL *)URL -{ - return [self initWithURL:URL protocols:nil]; -} - -- (instancetype)initWithURL:(NSURL *)URL protocols:(NSArray *)protocols -{ - NSMutableURLRequest *request; - if (URL) { - // Build a mutable request so we can fill the cookie header. - request = [NSMutableURLRequest requestWithURL:URL]; - - // We load cookies from sharedHTTPCookieStorage (shared with XHR and - // fetch). To get HTTPS-only cookies for wss URLs, replace wss with https - // in the URL. - NSURLComponents *components = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:true]; - if ([components.scheme isEqualToString:@"wss"]) { - components.scheme = @"https"; - } - - // Load and set the cookie header. - NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:components.URL]; - [request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]]; - } - return [self initWithURLRequest:request protocols:protocols]; -} - -- (void)_RCTSR_commonInit -{ - NSString *scheme = _url.scheme.lowercaseString; - assert( - [scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || - [scheme isEqualToString:@"https"]); - - if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { - _secure = YES; - } - - _readyState = RCTSR_CONNECTING; - _consumerStopped = YES; - _webSocketVersion = 13; - - _workQueue = dispatch_queue_create("com.facebook.react.SRWebSocket", DISPATCH_QUEUE_SERIAL); - - // Going to set a specific on the queue so we can validate we're on the work queue - dispatch_queue_set_specific(_workQueue, (__bridge void *)self, (__bridge void *)_workQueue, NULL); - - _delegateDispatchQueue = dispatch_get_main_queue(); - - _readBuffer = [NSMutableData new]; - _outputBuffer = [NSMutableData new]; - - _currentFrameData = [NSMutableData new]; - - _consumers = [NSMutableArray new]; - - _consumerPool = [RCTSRIOConsumerPool new]; - - _scheduledRunloops = [NSMutableSet new]; - - [self _initializeStreams]; - - // default handlers -} - -- (void)assertOnWorkQueue -{ - assert(dispatch_get_specific((__bridge void *)self) == (__bridge void *)_workQueue); -} - -- (void)dealloc -{ - _inputStream.delegate = nil; - _outputStream.delegate = nil; - - [_inputStream close]; - [_outputStream close]; - - if (_receivedHTTPHeaders) { - CFRelease(_receivedHTTPHeaders); - _receivedHTTPHeaders = NULL; - } -} - -#ifndef NDEBUG - -- (void)setReadyState:(RCTSRReadyState)aReadyState -{ - [self willChangeValueForKey:@"readyState"]; - assert(aReadyState > _readyState); - _readyState = aReadyState; - [self didChangeValueForKey:@"readyState"]; -} - -#endif - -- (void)open -{ - assert(_url); - RCTAssert(_readyState == RCTSR_CONNECTING, @"Cannot call -(void)open on RCTSRWebSocket more than once"); - - _selfRetain = self; - - [self _connect]; -} - -// Calls block on delegate queue -- (void)_performDelegateBlock:(dispatch_block_t)block -{ - if (_delegateOperationQueue) { - [_delegateOperationQueue addOperationWithBlock:block]; - } else { - assert(_delegateDispatchQueue); - dispatch_async(_delegateDispatchQueue, block); - } -} - -- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue -{ - _delegateDispatchQueue = queue; -} - -- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage -{ - NSString *acceptHeader = - CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); - - if (acceptHeader == nil) { - return NO; - } - - NSString *concattedString = [_secKey stringByAppendingString:RCTSRWebSocketAppendToSecKeyString]; - NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; - - return [acceptHeader isEqualToString:expectedAccept]; -} - -- (void)_HTTPHeadersDidFinish -{ - NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); - - if (responseCode >= 400) { - RCTSRLog(@"Request failed with response code %ld", responseCode); - [self _failWithError:[NSError errorWithDomain:RCTSRWebSocketErrorDomain - code:2132 - userInfo:@{ - NSLocalizedDescriptionKey : - [NSString stringWithFormat:@"received bad response code from server %ld", - (long)responseCode], - RCTSRHTTPResponseErrorKey : @(responseCode) - }]]; - return; - } - - if (![self _checkHandshake:_receivedHTTPHeaders]) { - [self _failWithError:[NSError errorWithDomain:RCTSRWebSocketErrorDomain - code:2133 - userInfo:@{ - NSLocalizedDescriptionKey : - [NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] - }]]; - return; - } - - NSString *negotiatedProtocol = - CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); - if (negotiatedProtocol) { - // Make sure we requested the protocol - if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { - [self _failWithError: - [NSError errorWithDomain:RCTSRWebSocketErrorDomain - code:2133 - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] - }]]; - return; - } - - _protocol = negotiatedProtocol; - } - - self.readyState = RCTSR_OPEN; - - if (!_didFail) { - [self _readFrameNew]; - } - - [self _performDelegateBlock:^{ - if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { - [self.delegate webSocketDidOpen:self]; - }; - }]; -} - -- (void)_readHTTPHeader -{ - if (_receivedHTTPHeaders == NULL) { - _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); - } - - [self _readUntilHeaderCompleteWithCallback:^(RCTSRWebSocket *socket, NSData *data) { - CFHTTPMessageAppendBytes(self->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); - - if (CFHTTPMessageIsHeaderComplete(self->_receivedHTTPHeaders)) { - RCTSRLog( - @"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); - [socket _HTTPHeadersDidFinish]; - } else { - [socket _readHTTPHeader]; - } - }]; -} - -- (void)didConnect -{ - RCTSRLog(@"Connected"); - CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); - - // Set host first so it defaults - CFHTTPMessageSetHeaderFieldValue( - request, - CFSTR("Host"), - (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); - - NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; - int result __unused = SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); - assert(result == 0); - _secKey = [keyBytes base64EncodedStringWithOptions:0]; - assert([_secKey length] == 24); - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); - CFHTTPMessageSetHeaderFieldValue( - request, - CFSTR("Sec-WebSocket-Version"), - (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.RCTSR_origin); - - if (_requestedProtocols && _requestedProtocols.count > 0) { - CFHTTPMessageSetHeaderFieldValue( - request, - CFSTR("Sec-WebSocket-Protocol"), - (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); - } - - [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); - }]; - - NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); - - CFRelease(request); - - [self _writeData:message]; - [self _readHTTPHeader]; -} - -- (void)_initializeStreams -{ - assert(_url.port.unsignedIntValue <= UINT32_MAX); - uint32_t port = _url.port.unsignedIntValue; - if (port == 0) { - if (!_secure) { - port = 80; - } else { - port = 443; - } - } - NSString *host = _url.host; - - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - - CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); - - _outputStream = CFBridgingRelease(writeStream); - _inputStream = CFBridgingRelease(readStream); - - if (_secure) { - NSMutableDictionary *SSLOptions = [NSMutableDictionary new]; - - [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL - forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; - - // If we're using pinned certs, don't validate the certificate chain - if (_urlRequest.RCTSR_SSLPinnedCertificates.count) { - [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; - } - -#if DEBUG - [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; - RCTLogInfo(@"SocketRocket: In debug mode. Allowing connection to any root cert"); -#endif - - [_outputStream setProperty:SSLOptions forKey:(__bridge id)kCFStreamPropertySSLSettings]; - } - - _inputStream.delegate = self; - _outputStream.delegate = self; -} - -- (void)_connect -{ - if (!_scheduledRunloops.count) { - [self scheduleInRunLoop:[NSRunLoop RCTSR_networkRunLoop] forMode:NSDefaultRunLoopMode]; - } - - [_outputStream open]; - [_inputStream open]; -} - -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode -{ - [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; - [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; - - [_scheduledRunloops addObject:@[ aRunLoop, mode ]]; -} - -- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode -{ - [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; - [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; - - [_scheduledRunloops removeObject:@[ aRunLoop, mode ]]; -} - -- (void)close -{ - [self closeWithCode:RCTSRStatusCodeNormal reason:nil]; -} - -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason -{ - assert(code); - dispatch_async(_workQueue, ^{ - if (self.readyState == RCTSR_CLOSING || self.readyState == RCTSR_CLOSED) { - return; - } - - BOOL wasConnecting = self.readyState == RCTSR_CONNECTING; - - self.readyState = RCTSR_CLOSING; - - RCTSRLog(@"Closing with code %ld reason %@", code, reason); - - if (wasConnecting) { - [self _disconnect]; - return; - } - - size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; - NSData *payload = mutablePayload; - - ((uint16_t *)mutablePayload.mutableBytes)[0] = NSSwapBigShortToHost(code); - - if (reason) { - NSRange remainingRange = {0}; - - NSUInteger usedLength = 0; - - BOOL success __unused = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) - maxLength:payload.length - sizeof(uint16_t) - usedLength:&usedLength - encoding:NSUTF8StringEncoding - options:NSStringEncodingConversionExternalRepresentation - range:NSMakeRange(0, reason.length) - remainingRange:&remainingRange]; - - assert(success); - assert(remainingRange.length == 0); - - if (usedLength != maxMsgSize) { - payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; - } - } - - [self _sendFrameWithOpcode:RCTSROpCodeConnectionClose data:payload]; - }); -} - -- (void)_closeWithProtocolError:(NSString *)message -{ - // Need to shunt this on the _callbackQueue first to see if they received any messages - [self _performDelegateBlock:^{ - [self closeWithCode:RCTSRStatusCodeProtocolError reason:message]; - dispatch_async(self->_workQueue, ^{ - [self _disconnect]; - }); - }]; -} - -- (void)_failWithError:(NSError *)error -{ - dispatch_async(_workQueue, ^{ - if (self.readyState != RCTSR_CLOSED) { - self->_failed = YES; - [self _performDelegateBlock:^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { - [self.delegate webSocket:self didFailWithError:error]; - } - }]; - - self.readyState = RCTSR_CLOSED; - - RCTSRLog(@"Failing with error %@", error.localizedDescription); - - [self _disconnect]; - [self _scheduleCleanup]; - } - }); -} - -- (void)_writeData:(NSData *)data -{ - [self assertOnWorkQueue]; - - if (_closeWhenFinishedWriting) { - return; - } - [_outputBuffer appendData:data]; - [self _pumpWriting]; -} - -- (void)send:(id)data -{ - RCTAssert(self.readyState != RCTSR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); - if (nil == data) { - return; - } - // TODO: maybe not copy this for performance - data = [data copy]; - dispatch_async(_workQueue, ^{ - if ([data isKindOfClass:[NSString class]]) { - [self _sendFrameWithOpcode:RCTSROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; - } else if ([data isKindOfClass:[NSData class]]) { - [self _sendFrameWithOpcode:RCTSROpCodeBinaryFrame data:data]; - } else { - assert(NO); - } - }); -} - -- (void)sendPing:(NSData *)data -{ - RCTAssert(self.readyState == RCTSR_OPEN, @"Invalid State: Cannot call send: until connection is open"); - // TODO: maybe not copy this for performance - data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty - dispatch_async(_workQueue, ^{ - [self _sendFrameWithOpcode:RCTSROpCodePing data:data]; - }); -} - -- (void)handlePing:(NSData *)pingData -{ - // Need to pingpong this off _callbackQueue first to make sure messages happen in order - [self _performDelegateBlock:^{ - dispatch_async(self->_workQueue, ^{ - [self _sendFrameWithOpcode:RCTSROpCodePong data:pingData]; - }); - }]; -} - -- (void)handlePong:(NSData *)pongData -{ - RCTSRLog(@"Received pong"); - [self _performDelegateBlock:^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) { - [self.delegate webSocket:self didReceivePong:pongData]; - } - }]; -} - -- (void)_handleMessage:(id)message -{ - RCTSRLog(@"Received message"); - [self _performDelegateBlock:^{ - [self.delegate webSocket:self didReceiveMessage:message]; - }]; -} - -static inline BOOL closeCodeIsValid(int closeCode) -{ - if (closeCode < 1000) { - return NO; - } - - if (closeCode >= 1000 && closeCode <= 1011) { - if (closeCode == 1004 || closeCode == 1005 || closeCode == 1006) { - return NO; - } - return YES; - } - - if (closeCode >= 3000 && closeCode <= 3999) { - return YES; - } - - if (closeCode >= 4000 && closeCode <= 4999) { - return YES; - } - - return NO; -} - -// Note from RFC: -// -// If there is a body, the first two -// bytes of the body MUST be a 2-byte unsigned integer (in network byte -// order) representing a status code with value /code/ defined in -// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 -// encoded data with value /reason/, the interpretation of which is not -// defined by this specification. - -- (void)handleCloseWithData:(NSData *)data -{ - size_t dataSize = data.length; - __block uint16_t closeCode = 0; - - RCTSRLog(@"Received close frame"); - - if (dataSize == 1) { - // TODO: handle error - [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; - return; - } else if (dataSize >= 2) { - [data getBytes:&closeCode length:sizeof(closeCode)]; - _closeCode = NSSwapBigShortToHost(closeCode); - if (!closeCodeIsValid(_closeCode)) { - [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; - return; - } - if (dataSize > 2) { - _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] - encoding:NSUTF8StringEncoding]; - if (!_closeReason) { - [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; - return; - } - } - } else { - _closeCode = RCTSRStatusNoStatusReceived; - } - - [self assertOnWorkQueue]; - - if (self.readyState == RCTSR_OPEN) { - [self closeWithCode:1000 reason:nil]; - } - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); -} - -- (void)_disconnect -{ - [self assertOnWorkQueue]; - RCTSRLog(@"Trying to disconnect"); - _closeWhenFinishedWriting = YES; - [self _pumpWriting]; -} - -- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode -{ - // copy frameData before handling, - // to avoid concurrent updates to the value at the pointer - frameData = [frameData copy]; - - // Check that the current data is valid UTF8 - - BOOL isControlFrame = - (opcode == RCTSROpCodePing || opcode == RCTSROpCodePong || opcode == RCTSROpCodeConnectionClose); - if (!isControlFrame) { - [self _readFrameNew]; - } else { - dispatch_async(_workQueue, ^{ - [self _readFrameContinue]; - }); - } - - switch (opcode) { - case RCTSROpCodeTextFrame: { - NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; - if (str == nil && frameData) { - [self closeWithCode:RCTSRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); - - return; - } - [self _handleMessage:str]; - break; - } - case RCTSROpCodeBinaryFrame: - [self _handleMessage:[frameData copy]]; - break; - case RCTSROpCodeConnectionClose: - [self handleCloseWithData:frameData]; - break; - case RCTSROpCodePing: - [self handlePing:frameData]; - break; - case RCTSROpCodePong: - [self handlePong:frameData]; - break; - default: - [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; - // TODO: Handle invalid opcode - break; - } -} - -- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData -{ - assert(frame_header.opcode != 0); - - if (self.readyState != RCTSR_OPEN) { - return; - } - - BOOL isControlFrame = - (frame_header.opcode == RCTSROpCodePing || frame_header.opcode == RCTSROpCodePong || - frame_header.opcode == RCTSROpCodeConnectionClose); - - if (isControlFrame && !frame_header.fin) { - [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; - return; - } - - if (isControlFrame && frame_header.payload_length >= 126) { - [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; - return; - } - - if (!isControlFrame) { - _currentFrameOpcode = frame_header.opcode; - _currentFrameCount += 1; - } - - if (frame_header.payload_length == 0) { - if (isControlFrame) { - [self _handleFrameWithData:curData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO: add assert that opcode is not a control; - [self _readFrameContinue]; - } - } - } else { - assert(frame_header.payload_length <= SIZE_T_MAX); - [self _addConsumerWithDataLength:(size_t)frame_header.payload_length - callback:^(RCTSRWebSocket *socket, NSData *newData) { - if (isControlFrame) { - [socket _handleFrameWithData:newData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [socket _handleFrameWithData:socket->_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO: add assert that opcode is not a control; - [socket _readFrameContinue]; - } - } - } - readToCurrentFrame:!isControlFrame - unmaskBytes:frame_header.masked]; - } -} - -/* From RFC: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ - */ - -static const uint8_t RCTSRFinMask = 0x80; -static const uint8_t RCTSROpCodeMask = 0x0F; -static const uint8_t RCTSRRsvMask = 0x70; -static const uint8_t RCTSRMaskMask = 0x80; -static const uint8_t RCTSRPayloadLenMask = 0x7F; - -- (void)_readFrameContinue -{ - assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); - - [self - _addConsumerWithDataLength:2 - callback:^(RCTSRWebSocket *socket, NSData *data) { - __block frame_header header = {0}; - - const uint8_t *headerBuffer = data.bytes; - assert(data.length >= 2); - - if (headerBuffer[0] & RCTSRRsvMask) { - [socket _closeWithProtocolError:@"Server used RSV bits"]; - return; - } - - uint8_t receivedOpcode = (RCTSROpCodeMask & headerBuffer[0]); - - BOOL isControlFrame = - (receivedOpcode == RCTSROpCodePing || receivedOpcode == RCTSROpCodePong || - receivedOpcode == RCTSROpCodeConnectionClose); - - if (!isControlFrame && receivedOpcode != 0 && socket->_currentFrameCount > 0) { - [socket _closeWithProtocolError: - @"all data frames after the initial data frame must have opcode 0"]; - return; - } - - if (receivedOpcode == 0 && socket->_currentFrameCount == 0) { - [socket _closeWithProtocolError:@"cannot continue a message"]; - return; - } - - header.opcode = receivedOpcode == 0 ? socket->_currentFrameOpcode : receivedOpcode; - - header.fin = !!(RCTSRFinMask & headerBuffer[0]); - - header.masked = !!(RCTSRMaskMask & headerBuffer[1]); - header.payload_length = RCTSRPayloadLenMask & headerBuffer[1]; - - headerBuffer = NULL; - - if (header.masked) { - [socket _closeWithProtocolError:@"Client must receive unmasked data"]; - } - - size_t extra_bytes_needed = header.masked ? sizeof(self->_currentReadMaskKey) : 0; - - if (header.payload_length == 126) { - extra_bytes_needed += sizeof(uint16_t); - } else if (header.payload_length == 127) { - extra_bytes_needed += sizeof(uint64_t); - } - - if (extra_bytes_needed == 0) { - [socket _handleFrameHeader:header curData:socket->_currentFrameData]; - } else { - [socket - _addConsumerWithDataLength:extra_bytes_needed - callback:^(RCTSRWebSocket *_socket, NSData *_data) { - size_t mapped_size __unused = _data.length; - const void *mapped_buffer = _data.bytes; - size_t offset = 0; - - if (header.payload_length == 126) { - assert(mapped_size >= sizeof(uint16_t)); - uint16_t newLen = - NSSwapBigShortToHost(*(uint16_t *)(mapped_buffer)); - header.payload_length = newLen; - offset += sizeof(uint16_t); - } else if (header.payload_length == 127) { - assert(mapped_size >= sizeof(uint64_t)); - header.payload_length = - NSSwapBigLongLongToHost(*(uint64_t *)(mapped_buffer)); - offset += sizeof(uint64_t); - } else { - assert(header.payload_length < 126 && header.payload_length >= 0); - } - - if (header.masked) { - assert( - mapped_size >= sizeof(self->_currentReadMaskOffset) + offset); - memcpy( - _socket->_currentReadMaskKey, - ((uint8_t *)mapped_buffer) + offset, - sizeof(_socket->_currentReadMaskKey)); - } - - [_socket _handleFrameHeader:header - curData:_socket->_currentFrameData]; - } - readToCurrentFrame:NO - unmaskBytes:NO]; - } - } - readToCurrentFrame:NO - unmaskBytes:NO]; -} - -- (void)_readFrameNew -{ - dispatch_async(_workQueue, ^{ - self->_currentFrameData.length = 0; - - self->_currentFrameOpcode = 0; - self->_currentFrameCount = 0; - self->_readOpCount = 0; - self->_currentStringScanPosition = 0; - - [self _readFrameContinue]; - }); -} - -- (void)_pumpWriting -{ - [self assertOnWorkQueue]; - - NSUInteger dataLength = _outputBuffer.length; - if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { - NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset - maxLength:dataLength - _outputBufferOffset]; - if (bytesWritten == -1) { - [self _failWithError:[NSError errorWithDomain:RCTSRWebSocketErrorDomain - code:2145 - userInfo:@{NSLocalizedDescriptionKey : @"Error writing to stream"}]]; - return; - } - - _outputBufferOffset += bytesWritten; - - if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { - _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset - length:_outputBuffer.length - _outputBufferOffset]; - _outputBufferOffset = 0; - } - } - - if (_closeWhenFinishedWriting && _outputBuffer.length - _outputBufferOffset == 0 && - (_inputStream.streamStatus != NSStreamStatusNotOpen && _inputStream.streamStatus != NSStreamStatusClosed) && - !_sentClose) { - _sentClose = YES; - - [self _scheduleCleanup]; - - if (!_failed) { - [self _performDelegateBlock:^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES]; - } - }]; - } - } -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback -{ - [self assertOnWorkQueue]; - [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; -} - -- (void)_addConsumerWithDataLength:(size_t)dataLength - callback:(data_callback)callback - readToCurrentFrame:(BOOL)readToCurrentFrame - unmaskBytes:(BOOL)unmaskBytes -{ - [self assertOnWorkQueue]; - assert(dataLength); - - [_consumers addObject:[_consumerPool consumerWithScanner:nil - handler:callback - bytesNeeded:dataLength - readToCurrentFrame:readToCurrentFrame - unmaskBytes:unmaskBytes]]; - [self _pumpScanner]; -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength -{ - [self assertOnWorkQueue]; - [_consumers addObject:[_consumerPool consumerWithScanner:consumer - handler:callback - bytesNeeded:dataLength - readToCurrentFrame:NO - unmaskBytes:NO]]; - [self _pumpScanner]; -} - -static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; - -- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler -{ - [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; -} - -- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler -{ - // TODO: optimize so this can continue from where we last searched - stream_scanner consumer = ^size_t(NSData *data) { - __block size_t found_size = 0; - __block size_t match_count = 0; - - size_t size = data.length; - const unsigned char *buffer = data.bytes; - for (size_t i = 0; i < size; i++) { - if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { - match_count += 1; - if (match_count == length) { - found_size = i + 1; - break; - } - } else { - match_count = 0; - } - } - return found_size; - }; - [self _addConsumerWithScanner:consumer callback:dataHandler]; -} - -// Returns true if did work -- (BOOL)_innerPumpScanner -{ - BOOL didWork = NO; - - if (self.readyState >= RCTSR_CLOSING) { - return didWork; - } - - if (!_consumers.count) { - return didWork; - } - - size_t curSize = _readBuffer.length - _readBufferOffset; - if (!curSize) { - return didWork; - } - - RCTSRIOConsumer *consumer = _consumers[0]; - - size_t bytesNeeded = consumer.bytesNeeded; - - size_t foundSize = 0; - if (consumer.consumer) { - NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset - length:_readBuffer.length - _readBufferOffset - freeWhenDone:NO]; - foundSize = consumer.consumer(tempView); - } else { - assert(consumer.bytesNeeded); - if (curSize >= bytesNeeded) { - foundSize = bytesNeeded; - } else if (consumer.readToCurrentFrame) { - foundSize = curSize; - } - } - - NSData *slice = nil; - if (consumer.readToCurrentFrame || foundSize) { - NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); - slice = [_readBuffer subdataWithRange:sliceRange]; - - _readBufferOffset += foundSize; - - if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { - _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset - length:_readBuffer.length - _readBufferOffset]; - _readBufferOffset = 0; - } - - if (consumer.unmaskBytes) { - NSMutableData *mutableSlice = [slice mutableCopy]; - - NSUInteger len = mutableSlice.length; - uint8_t *bytes = mutableSlice.mutableBytes; - - for (NSUInteger i = 0; i < len; i++) { - bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; - _currentReadMaskOffset += 1; - } - - slice = mutableSlice; - } - - if (consumer.readToCurrentFrame) { - [_currentFrameData appendData:slice]; - - _readOpCount += 1; - - if (_currentFrameOpcode == RCTSROpCodeTextFrame) { - // Validate UTF8 stuff. - size_t currentDataSize = _currentFrameData.length; - if (_currentFrameOpcode == RCTSROpCodeTextFrame && currentDataSize > 0) { - // TODO: Optimize this. Don't really have to copy all the data each time - - size_t scanSize = currentDataSize - _currentStringScanPosition; - - NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; - int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); - - if (valid_utf8_size == -1) { - [self closeWithCode:RCTSRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); - return didWork; - } else { - _currentStringScanPosition += valid_utf8_size; - } - } - } - - consumer.bytesNeeded -= foundSize; - - if (consumer.bytesNeeded == 0) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, nil); - [_consumerPool returnConsumer:consumer]; - didWork = YES; - } - } else if (foundSize) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, slice); - [_consumerPool returnConsumer:consumer]; - didWork = YES; - } - } - return didWork; -} - -- (void)_pumpScanner -{ - [self assertOnWorkQueue]; - - if (!_isPumping) { - _isPumping = YES; - } else { - return; - } - - while ([self _innerPumpScanner]) { - } - - _isPumping = NO; -} - -//#define NOMASK - -static const size_t RCTSRFrameHeaderOverhead = 32; - -- (void)_sendFrameWithOpcode:(RCTSROpCode)opcode data:(NSData *)data -{ - [self assertOnWorkQueue]; - - if (nil == data) { - return; - } - - size_t payloadLength = [data length]; - - NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + RCTSRFrameHeaderOverhead]; - if (!frame) { - [self closeWithCode:RCTSRStatusCodeMessageTooBig reason:@"Message too big"]; - return; - } - uint8_t *frame_buffer = (uint8_t *)frame.mutableBytes; - - // set fin - frame_buffer[0] = RCTSRFinMask | opcode; - - BOOL useMask = YES; -#ifdef NOMASK - useMask = NO; -#endif - - if (useMask) { - // set the mask and header - frame_buffer[1] |= RCTSRMaskMask; - } - - size_t frame_buffer_size = 2; - - const uint8_t *unmasked_payload = (uint8_t *)[data bytes]; - - if (payloadLength < 126) { - frame_buffer[1] |= payloadLength; - } else if (payloadLength <= UINT16_MAX) { - frame_buffer[1] |= 126; - *((uint16_t *)(frame_buffer + frame_buffer_size)) = NSSwapBigShortToHost((uint16_t)payloadLength); - frame_buffer_size += sizeof(uint16_t); - } else { - frame_buffer[1] |= 127; - *((uint64_t *)(frame_buffer + frame_buffer_size)) = NSSwapBigLongLongToHost((uint64_t)payloadLength); - frame_buffer_size += sizeof(uint64_t); - } - - if (!useMask) { - for (size_t i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i]; - frame_buffer_size += 1; - } - } else { - uint8_t *mask_key = frame_buffer + frame_buffer_size; - int result __unused = SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); - assert(result == 0); - frame_buffer_size += sizeof(uint32_t); - - // TODO: could probably optimize this with SIMD - for (size_t i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; - frame_buffer_size += 1; - } - } - - assert(frame_buffer_size <= [frame length]); - frame.length = frame_buffer_size; - - [self _writeData:frame]; -} - -- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode -{ - if (_secure && !_pinnedCertFound && - (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { - NSArray *sslCerts = _urlRequest.RCTSR_SSLPinnedCertificates; - if (sslCerts) { - SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; - if (secTrust) { - NSInteger numCerts = SecTrustGetCertificateCount(secTrust); - for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); - NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); - - for (id ref in sslCerts) { - SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; - NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); - - if ([trustedCertData isEqualToData:certData]) { - _pinnedCertFound = YES; - break; - } - } - } - } - - if (!_pinnedCertFound) { - dispatch_async(_workQueue, ^{ - [self _failWithError:[NSError errorWithDomain:RCTSRWebSocketErrorDomain - code:23556 - userInfo:@{ - NSLocalizedDescriptionKey : - [NSString stringWithFormat:@"Invalid server cert"] - }]]; - }); - return; - } - } - } - - // _workQueue cannot be NULL - if (!_workQueue) { - return; - } - __weak typeof(self) weakSelf = self; - dispatch_async(_workQueue, ^{ - typeof(self) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongSelf safeHandleEvent:eventCode stream:aStream]; - }); -} - -- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream -{ - switch (eventCode) { - case NSStreamEventOpenCompleted: { - RCTSRLog(@"NSStreamEventOpenCompleted %@", aStream); - if (self.readyState >= RCTSR_CLOSING) { - return; - } - assert(self->_readBuffer); - - if (self.readyState == RCTSR_CONNECTING && aStream == self->_inputStream) { - [self didConnect]; - } - [self _pumpWriting]; - [self _pumpScanner]; - break; - } - - case NSStreamEventErrorOccurred: { - RCTSRLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [aStream.streamError copy]); - // TODO: specify error better! - [self _failWithError:aStream.streamError]; - self->_readBufferOffset = 0; - self->_readBuffer.length = 0; - break; - } - - case NSStreamEventEndEncountered: { - [self _pumpScanner]; - RCTSRLog(@"NSStreamEventEndEncountered %@", aStream); - if (aStream.streamError) { - [self _failWithError:aStream.streamError]; - } else { - dispatch_async(self->_workQueue, ^{ - if (self.readyState != RCTSR_CLOSED) { - self.readyState = RCTSR_CLOSED; - [self _scheduleCleanup]; - } - - if (!self->_sentClose && !self->_failed) { - self->_sentClose = YES; - // If we get closed in this state it's probably not clean because we should be sending this when we send - // messages - [self _performDelegateBlock:^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self - didCloseWithCode:RCTSRStatusCodeGoingAway - reason:@"Stream end encountered" - wasClean:NO]; - } - }]; - } - }); - } - - break; - } - - case NSStreamEventHasBytesAvailable: { - RCTSRLog(@"NSStreamEventHasBytesAvailable %@", aStream); - const int bufferSize = 2048; - uint8_t buffer[bufferSize]; - - while (self->_inputStream.hasBytesAvailable) { - NSInteger bytes_read = [self->_inputStream read:buffer maxLength:bufferSize]; - - if (bytes_read > 0) { - [self->_readBuffer appendBytes:buffer length:bytes_read]; - } else if (bytes_read < 0) { - [self _failWithError:self->_inputStream.streamError]; - } - - if (bytes_read != bufferSize) { - break; - } - }; - [self _pumpScanner]; - break; - } - - case NSStreamEventHasSpaceAvailable: { - RCTSRLog(@"NSStreamEventHasSpaceAvailable %@", aStream); - [self _pumpWriting]; - break; - } - - default: - RCTSRLog(@"(default) %@", aStream); - break; - } -} - -- (void)_scheduleCleanup -{ - if (_cleanupScheduled) { - return; - } - - _cleanupScheduled = YES; - - // Cleanup NSStream's delegate in the same RunLoop used by the streams themselves: - // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc - NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) - target:self - selector:@selector(_cleanupSelfReference:) - userInfo:nil - repeats:NO]; - [[NSRunLoop RCTSR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; -} - -- (void)_cleanupSelfReference:(NSTimer *)timer -{ - // Remove the streams, right now, from the networkRunLoop - [_inputStream close]; - [_outputStream close]; - - // Unschedule from RunLoop - for (NSArray *runLoop in [_scheduledRunloops copy]) { - [self unscheduleFromRunLoop:runLoop[0] forMode:runLoop[1]]; - } - - // Nuke NSStream's delegate - _inputStream.delegate = nil; - _outputStream.delegate = nil; - - // Cleanup selfRetain in the same GCD queue as usual - dispatch_async(_workQueue, ^{ - self->_selfRetain = nil; - }); -} - -@end - -@implementation RCTSRIOConsumer - -- (void)setupWithScanner:(stream_scanner)scanner - handler:(data_callback)handler - bytesNeeded:(size_t)bytesNeeded - readToCurrentFrame:(BOOL)readToCurrentFrame - unmaskBytes:(BOOL)unmaskBytes -{ - _consumer = [scanner copy]; - _handler = [handler copy]; - _bytesNeeded = bytesNeeded; - _readToCurrentFrame = readToCurrentFrame; - _unmaskBytes = unmaskBytes; - assert(_consumer || _bytesNeeded); -} - -@end - -@implementation RCTSRIOConsumerPool { - NSUInteger _poolSize; - NSMutableArray *_bufferedConsumers; -} - -- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize -{ - if ((self = [super init])) { - _poolSize = poolSize; - _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; - } - return self; -} - -- (instancetype)init -{ - return [self initWithBufferCapacity:8]; -} - -- (RCTSRIOConsumer *)consumerWithScanner:(stream_scanner)scanner - handler:(data_callback)handler - bytesNeeded:(size_t)bytesNeeded - readToCurrentFrame:(BOOL)readToCurrentFrame - unmaskBytes:(BOOL)unmaskBytes -{ - RCTSRIOConsumer *consumer = nil; - if (_bufferedConsumers.count) { - consumer = _bufferedConsumers.lastObject; - [_bufferedConsumers removeLastObject]; - } else { - consumer = [RCTSRIOConsumer new]; - } - - [consumer setupWithScanner:scanner - handler:handler - bytesNeeded:bytesNeeded - readToCurrentFrame:readToCurrentFrame - unmaskBytes:unmaskBytes]; - - return consumer; -} - -- (void)returnConsumer:(RCTSRIOConsumer *)consumer -{ - if (_bufferedConsumers.count < _poolSize) { - [_bufferedConsumers addObject:consumer]; - } -} - -@end - -@implementation NSURLRequest (CertificateAdditions) - -- (NSArray *)RCTSR_SSLPinnedCertificates -{ - return [NSURLProtocol propertyForKey:@"RCTSR_SSLPinnedCertificates" inRequest:self]; -} - -@end - -@implementation NSMutableURLRequest (CertificateAdditions) - -- (NSArray *)RCTSR_SSLPinnedCertificates -{ - return [NSURLProtocol propertyForKey:@"RCTSR_SSLPinnedCertificates" inRequest:self]; -} - -- (void)setRCTSR_SSLPinnedCertificates:(NSArray *)RCTSR_SSLPinnedCertificates -{ - [NSURLProtocol setProperty:RCTSR_SSLPinnedCertificates forKey:@"RCTSR_SSLPinnedCertificates" inRequest:self]; -} - -@end - -@implementation NSURL (RCTSRWebSocket) - -- (NSString *)RCTSR_origin -{ - NSString *scheme = self.scheme.lowercaseString; - - if ([scheme isEqualToString:@"wss"]) { - scheme = @"https"; - } else if ([scheme isEqualToString:@"ws"]) { - scheme = @"http"; - } - - int defaultPort = ([scheme isEqualToString:@"https"] ? 443 : [scheme isEqualToString:@"http"] ? 80 : -1); - int port = self.port.intValue; - if (port > 0 && port != defaultPort) { - return [NSString stringWithFormat:@"%@://%@:%d", scheme, self.host, port]; - } else { - return [NSString stringWithFormat:@"%@://%@", scheme, self.host]; - } -} - -@end - -static _RCTSRRunLoopThread *networkThread = nil; -static NSRunLoop *networkRunLoop = nil; - -@implementation NSRunLoop (RCTSRWebSocket) - -+ (NSRunLoop *)RCTSR_networkRunLoop -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - networkThread = [_RCTSRRunLoopThread new]; - networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; - [networkThread start]; - networkRunLoop = networkThread.runLoop; - }); - - return networkRunLoop; -} - -@end - -@implementation _RCTSRRunLoopThread { - dispatch_group_t _waitGroup; -} - -@synthesize runLoop = _runLoop; - -- (instancetype)init -{ - if ((self = [super init])) { - _waitGroup = dispatch_group_create(); - dispatch_group_enter(_waitGroup); - } - return self; -} - -- (void)main -{ - @autoreleasepool { - _runLoop = [NSRunLoop currentRunLoop]; - dispatch_group_leave(_waitGroup); - - NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture] - interval:0.0 - target:self - selector:@selector(step) - userInfo:nil - repeats:NO]; - [_runLoop addTimer:timer forMode:NSDefaultRunLoopMode]; - - while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { - } - assert(NO); - } -} - -- (void)step -{ - // Does nothing -} - -- (NSRunLoop *)runLoop -{ - dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); - return _runLoop; -} - -@end diff --git a/React-Core.podspec b/React-Core.podspec index 92fcd54c8f5786..53e9e509ff6942 100644 --- a/React-Core.podspec +++ b/React-Core.podspec @@ -18,6 +18,7 @@ end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' folly_version = '2021.07.22.00' +socket_rocket_version = '0.6.0' boost_compiler_flags = '-Wno-documentation' use_hermes = ENV['USE_HERMES'] == '1' @@ -129,6 +130,7 @@ Pod::Spec.new do |s| s.dependency "React-perflogger", version s.dependency "React-jsi", version s.dependency "React-jsiexecutor", version + s.dependency "SocketRocket", socket_rocket_version s.dependency "Yoga" s.dependency "glog" diff --git a/React/CoreModules/RCTWebSocketExecutor.mm b/React/CoreModules/RCTWebSocketExecutor.mm index 9418dd63891e17..1b03cbcd37f86a 100644 --- a/React/CoreModules/RCTWebSocketExecutor.mm +++ b/React/CoreModules/RCTWebSocketExecutor.mm @@ -12,10 +12,10 @@ #import #import #import -#import #import #import +#import #import "CoreModulesPlugins.h" @@ -23,12 +23,12 @@ typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary *reply); -@interface RCTWebSocketExecutor () +@interface RCTWebSocketExecutor () @end @implementation RCTWebSocketExecutor { - RCTSRWebSocket *_socket; + SRWebSocket *_socket; dispatch_queue_t _jsQueue; NSMutableDictionary *_callbacks; dispatch_semaphore_t _socketOpenSemaphore; @@ -62,7 +62,7 @@ - (void)setUp } _jsQueue = dispatch_queue_create("com.facebook.react.WebSocketExecutor", DISPATCH_QUEUE_SERIAL); - _socket = [[RCTSRWebSocket alloc] initWithURL:_url]; + _socket = [[SRWebSocket alloc] initWithURL:_url]; _socket.delegate = self; _callbacks = [NSMutableDictionary new]; _injectedObjects = [NSMutableDictionary new]; @@ -112,7 +112,7 @@ - (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 == RCTSR_OPEN; + return connected == 0 && _socket.readyState == SR_OPEN; } - (BOOL)prepareJSRuntime @@ -131,7 +131,7 @@ - (BOOL)prepareJSRuntime return runtimeIsReady == 0 && initError == nil; } -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { NSError *error = nil; NSDictionary *reply = RCTJSONParse(message, &error); @@ -143,12 +143,12 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message } } -- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket +- (void)webSocketDidOpen:(SRWebSocket *)webSocket { dispatch_semaphore_signal(_socketOpenSemaphore); } -- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { dispatch_semaphore_signal(_socketOpenSemaphore); RCTLogInfo(@"WebSocket connection failed with error %@", error); @@ -173,7 +173,7 @@ - (void)sendMessage:(NSDictionary *)message onReply:(RCTWSMessag self->_callbacks[expectedID] = [callback copy]; NSMutableDictionary *messageWithID = [message mutableCopy]; messageWithID[@"id"] = expectedID; - [self->_socket send:RCTJSONStringify(messageWithID, NULL)]; + [self->_socket sendString:RCTJSONStringify(messageWithID, NULL) error:nil]; }); } @@ -270,7 +270,7 @@ - (void)invalidate - (BOOL)isValid { - return _socket != nil && _socket.readyState == RCTSR_OPEN; + return _socket != nil && _socket.readyState == SR_OPEN; } - (void)dealloc diff --git a/React/CoreModules/RCTWebSocketModule.mm b/React/CoreModules/RCTWebSocketModule.mm index c099328eaff69b..4451b9460e5bac 100644 --- a/React/CoreModules/RCTWebSocketModule.mm +++ b/React/CoreModules/RCTWebSocketModule.mm @@ -11,12 +11,12 @@ #import #import -#import #import +#import #import "CoreModulesPlugins.h" -@implementation RCTSRWebSocket (React) +@implementation SRWebSocket (React) - (NSNumber *)reactTag { @@ -30,12 +30,12 @@ - (void)setReactTag:(NSNumber *)reactTag @end -@interface RCTWebSocketModule () +@interface RCTWebSocketModule () @end @implementation RCTWebSocketModule { - NSMutableDictionary *_sockets; + NSMutableDictionary *_sockets; NSMutableDictionary> *_contentHandlers; } @@ -56,7 +56,7 @@ - (void)invalidate [super invalidate]; _contentHandlers = nil; - for (RCTSRWebSocket *socket in _sockets.allValues) { + for (SRWebSocket *socket in _sockets.allValues) { socket.delegate = nil; [socket close]; } @@ -90,7 +90,7 @@ - (void)invalidate }]; } - RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols]; + SRWebSocket *webSocket = [[SRWebSocket alloc] initWithURLRequest:request protocols:protocols]; [webSocket setDelegateDispatchQueue:[self methodQueue]]; webSocket.delegate = self; webSocket.reactTag = @(socketID); @@ -103,7 +103,7 @@ - (void)invalidate RCT_EXPORT_METHOD(send : (NSString *)message forSocketID : (double)socketID) { - [_sockets[@(socketID)] send:message]; + [_sockets[@(socketID)] sendString:message error:nil]; } RCT_EXPORT_METHOD(sendBinary : (NSString *)base64String forSocketID : (double)socketID) @@ -113,12 +113,12 @@ - (void)invalidate - (void)sendData:(NSData *)data forSocketID:(NSNumber *__nonnull)socketID { - [_sockets[socketID] send:data]; + [_sockets[socketID] sendData:data error:nil]; } RCT_EXPORT_METHOD(ping : (double)socketID) { - [_sockets[@(socketID)] sendPing:NULL]; + [_sockets[@(socketID)] sendPing:nil error:nil]; } RCT_EXPORT_METHOD(close : (double)code reason : (NSString *)reason socketID : (double)socketID) @@ -137,7 +137,7 @@ - (void)setContentHandler:(id)handler forSocketID:(N #pragma mark - RCTSRWebSocketDelegate methods -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { NSString *type; @@ -157,13 +157,13 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message [self sendEventWithName:@"websocketMessage" body:@{@"data" : message, @"type" : type, @"id" : webSocket.reactTag}]; } -- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket +- (void)webSocketDidOpen:(SRWebSocket *)webSocket { [self sendEventWithName:@"websocketOpen" body:@{@"id" : webSocket.reactTag, @"protocol" : webSocket.protocol ? webSocket.protocol : @""}]; } -- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { NSNumber *socketID = [webSocket reactTag]; _contentHandlers[socketID] = nil; @@ -173,7 +173,7 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error [self sendEventWithName:@"websocketFailed" body:body]; } -- (void)webSocket:(RCTSRWebSocket *)webSocket +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean diff --git a/React/CoreModules/React-CoreModules.podspec b/React/CoreModules/React-CoreModules.podspec index 90dc3fd242e37b..0b1659a48883aa 100644 --- a/React/CoreModules/React-CoreModules.podspec +++ b/React/CoreModules/React-CoreModules.podspec @@ -18,6 +18,7 @@ end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' folly_version = '2021.07.22.00' +socket_rocket_version = '0.6.0' header_search_paths = [ "\"$(PODS_TARGET_SRCROOT)/React/CoreModules\"", @@ -57,4 +58,5 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core", version s.dependency "React-jsi", version s.dependency 'React-RCTBlob' + s.dependency "SocketRocket", socket_rocket_version end diff --git a/React/DevSupport/RCTPackagerConnection.mm b/React/DevSupport/RCTPackagerConnection.mm index 199f8ac42ad320..08c0ca1dd445ec 100644 --- a/React/DevSupport/RCTPackagerConnection.mm +++ b/React/DevSupport/RCTPackagerConnection.mm @@ -23,7 +23,7 @@ #if RCT_DEV -#import +#import @interface RCTPackagerConnection () @end diff --git a/React/Inspector/RCTInspector.mm b/React/Inspector/RCTInspector.mm index 4e31ecbb180d73..896cf4ec8ea743 100644 --- a/React/Inspector/RCTInspector.mm +++ b/React/Inspector/RCTInspector.mm @@ -14,7 +14,6 @@ #import #import #import -#import #import using namespace facebook::react; diff --git a/React/Inspector/RCTInspectorPackagerConnection.m b/React/Inspector/RCTInspectorPackagerConnection.m index af5c223e47450b..3d16997273dd67 100644 --- a/React/Inspector/RCTInspectorPackagerConnection.m +++ b/React/Inspector/RCTInspectorPackagerConnection.m @@ -12,8 +12,8 @@ #import #import #import -#import #import +#import // This is a port of the Android impl, at // ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java @@ -24,10 +24,10 @@ @implementation RCTBundleStatus @end -@interface RCTInspectorPackagerConnection () { +@interface RCTInspectorPackagerConnection () { NSURL *_url; NSMutableDictionary *_inspectorConnections; - RCTSRWebSocket *_webSocket; + SRWebSocket *_webSocket; dispatch_queue_t _jsQueue; BOOL _closed; BOOL _suppressConnectionErrors; @@ -188,7 +188,7 @@ - (void)sendEvent:(NSString *)name payload:(id)payload } // analogous to InspectorPackagerConnection.Connection.onFailure(...) -- (void)webSocket:(__unused RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error +- (void)webSocket:(__unused SRWebSocket *)webSocket didFailWithError:(NSError *)error { if (_webSocket) { [self abort:@"Websocket exception" withCause:error]; @@ -199,7 +199,7 @@ - (void)webSocket:(__unused RCTSRWebSocket *)webSocket didFailWithError:(NSError } // analogous to InspectorPackagerConnection.Connection.onMessage(...) -- (void)webSocket:(__unused RCTSRWebSocket *)webSocket didReceiveMessage:(id)opaqueMessage +- (void)webSocket:(__unused SRWebSocket *)webSocket didReceiveMessage:(id)opaqueMessage { // warn but don't die on unrecognized messages if (![opaqueMessage isKindOfClass:[NSString class]]) { @@ -219,7 +219,7 @@ - (void)webSocket:(__unused RCTSRWebSocket *)webSocket didReceiveMessage:(id)opa } // analogous to InspectorPackagerConnection.Connection.onClosed(...) -- (void)webSocket:(__unused RCTSRWebSocket *)webSocket +- (void)webSocket:(__unused SRWebSocket *)webSocket didCloseWithCode:(__unused NSInteger)code reason:(__unused NSString *)reason wasClean:(__unused BOOL)wasClean @@ -244,9 +244,9 @@ - (void)connect } // The corresponding android code has a lot of custom config options for - // timeouts, but it appears the iOS RCTSRWebSocket API doesn't have the same - // implemented options. - _webSocket = [[RCTSRWebSocket alloc] initWithURL:_url]; + // 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 setDelegateDispatchQueue:_jsQueue]; _webSocket.delegate = self; [_webSocket open]; @@ -290,7 +290,7 @@ - (void)sendToPackager:(NSDictionary *)messageObject if (error) { RCTLogWarn(@"Couldn't send event to packager: %@", error); } else { - [strongSelf->_webSocket send:messageText]; + [strongSelf->_webSocket sendString:messageText error:nil]; } } }); diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index ce20ddac2c4222..d523568721b4b5 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -146,6 +146,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/CoreModulesHeaders (1000.0.0): - glog @@ -157,6 +158,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/Default (1000.0.0): - glog @@ -167,6 +169,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/DevSupport (1000.0.0): - glog @@ -180,6 +183,7 @@ PODS: - React-jsiexecutor (= 1000.0.0) - React-jsinspector (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTActionSheetHeaders (1000.0.0): - glog @@ -191,6 +195,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTAnimationHeaders (1000.0.0): - glog @@ -202,6 +207,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTBlobHeaders (1000.0.0): - glog @@ -213,6 +219,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTImageHeaders (1000.0.0): - glog @@ -224,6 +231,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTLinkingHeaders (1000.0.0): - glog @@ -235,6 +243,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTNetworkHeaders (1000.0.0): - glog @@ -246,6 +255,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTPushNotificationHeaders (1000.0.0): - glog @@ -257,6 +267,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTSettingsHeaders (1000.0.0): - glog @@ -268,6 +279,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTTextHeaders (1000.0.0): - glog @@ -279,6 +291,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTVibrationHeaders (1000.0.0): - glog @@ -290,6 +303,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-Core/RCTWebSocket (1000.0.0): - glog @@ -301,6 +315,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-perflogger (= 1000.0.0) + - SocketRocket (= 0.6.0) - Yoga - React-CoreModules (1000.0.0): - RCT-Folly (= 2021.07.22.00) @@ -311,6 +326,7 @@ PODS: - React-RCTBlob - React-RCTImage (= 1000.0.0) - ReactCommon/turbomodule/core (= 1000.0.0) + - SocketRocket (= 0.6.0) - React-cxxreact (1000.0.0): - boost (= 1.76.0) - DoubleConversion @@ -988,7 +1004,7 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: d68947eddece25638eb0f642d1b957c90388afd1 - FBReactNativeSpec: bfaf9508748c07260662ac4f7d773617856c3d45 + FBReactNativeSpec: 2f5aa501c727b9010f9602412c5ea3c6fbc550b5 Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 @@ -999,7 +1015,7 @@ SPEC CHECKSUMS: FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - hermes-engine: e33b68f27408adcf86e955eb0337f9349f4fb0ed + hermes-engine: 73feba57805baf1ad50a46da4119e7d3cef36d01 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OCMock: 9491e4bec59e0b267d52a9184ff5605995e74be8 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c @@ -1009,8 +1025,8 @@ SPEC CHECKSUMS: React: 2fc6c4c656cccd6753016528ad41199c16fd558e React-callinvoker: a7d5e883a83bb9bd3985b08be832c5e76451d18f React-Codegen: ecc7c203dcc86316ff12a865dbfc71190458b367 - React-Core: 279a6e5ee79e88faa99157169b560c49635973d7 - React-CoreModules: d3ee40954b381edc514301341e8b895febfc1848 + React-Core: 6ed76c248f07d2d65d8d15b33a75444ef6ff7938 + React-CoreModules: 9b9060df7f561e9c8f8364333c2ec645d7c698d2 React-cxxreact: aff243750dad852080636e615d7ae5639381735b React-Fabric: 6b5c30b6e60a85446cc5d3702fa262fd1fc15619 React-graphics: e70886fff4b79bec3745de761900a770029591f2