initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTDefines.h>
#if RCT_DEV // Only supported in dev mode
@class RCTReconnectingWebSocket;
@protocol RCTReconnectingWebSocketDelegate
- (void)reconnectingWebSocketDidOpen:(RCTReconnectingWebSocket *)webSocket;
- (void)reconnectingWebSocket:(RCTReconnectingWebSocket *)webSocket didReceiveMessage:(id)message;
/** Sent when the socket has closed due to error or clean shutdown. An automatic reconnect will start shortly. */
- (void)reconnectingWebSocketDidClose:(RCTReconnectingWebSocket *)webSocket;
@end
@interface RCTReconnectingWebSocket : NSObject
/** Delegate will be messaged on the given queue (required). */
- (instancetype)initWithURL:(NSURL *)url queue:(dispatch_queue_t)queue;
@property (nonatomic, weak) id<RCTReconnectingWebSocketDelegate> delegate;
- (void)send:(id)data;
- (void)start;
- (void)stop;
- (instancetype)initWithURL:(NSURL *)url __deprecated_msg("Use initWithURL:queue: instead");
/** @brief Must be set before -start to have effect */
@property (nonatomic, strong) dispatch_queue_t delegateDispatchQueue __deprecated_msg("Use initWithURL:queue: instead");
@end
#endif

View File

@@ -0,0 +1,144 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTReconnectingWebSocket.h"
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <fishhook/fishhook.h>
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
#import <os/log.h>
#endif /* __IPHONE_11_0 */
#import "RCTSRWebSocket.h"
#if RCT_DEV // Only supported in dev mode
static void (*orig_nwlog_legacy_v)(int, char*, va_list);
static void my_nwlog_legacy_v(int level, char *format, va_list args) {
static const uint buffer_size = 256;
static char buffer[buffer_size];
va_list copy;
va_copy(copy, args);
vsnprintf(buffer, buffer_size, format, copy);
va_end(copy);
if (strstr(buffer, "nw_connection_get_connected_socket_block_invoke") == NULL &&
strstr(buffer, "Connection has no connected handler") == NULL) {
orig_nwlog_legacy_v(level, format, args);
}
}
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
static void (*orig_os_log_error_impl)(void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, uint32_t size);
static void my_os_log_error_impl(void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, uint32_t size)
{
if (strstr(format, "TCP Conn %p Failed : error %ld:%d") == NULL) {
orig_os_log_error_impl(dso, log, type, format, buf, size);
}
}
#endif /* __IPHONE_11_0 */
@interface RCTReconnectingWebSocket () <RCTSRWebSocketDelegate>
@end
@implementation RCTReconnectingWebSocket {
NSURL *_url;
RCTSRWebSocket *_socket;
}
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
rebind_symbols((struct rebinding[1]){
{"nwlog_legacy_v", my_nwlog_legacy_v, (void *)&orig_nwlog_legacy_v}
}, 1);
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
rebind_symbols((struct rebinding[1]){
{"_os_log_error_impl", my_os_log_error_impl, (void *)&orig_os_log_error_impl}
}, 1);
#endif /* __IPHONE_11_0 */
});
}
- (instancetype)initWithURL:(NSURL *)url queue:(dispatch_queue_t)queue
{
if (self = [super init]) {
_url = url;
_delegateDispatchQueue = queue;
}
return self;
}
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url queue:dispatch_get_main_queue()];
}
- (void)send:(id)data
{
[_socket send:data];
}
- (void)start
{
[self stop];
_socket = [[RCTSRWebSocket alloc] initWithURL:_url];
_socket.delegate = self;
[_socket setDelegateDispatchQueue:_delegateDispatchQueue];
[_socket open];
}
- (void)stop
{
_socket.delegate = nil;
[_socket closeWithCode:1000 reason:@"Invalidated"];
_socket = nil;
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{
[_delegate reconnectingWebSocket:self didReceiveMessage:message];
}
- (void)reconnect
{
__weak RCTSRWebSocket *socket = _socket;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// Only reconnect if the observer wasn't stoppped while we were waiting
if (socket) {
[self start];
}
});
}
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
{
[_delegate reconnectingWebSocketDidOpen:self];
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
{
[_delegate reconnectingWebSocketDidClose:self];
[self reconnect];
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
{
[_delegate reconnectingWebSocketDidClose:self];
[self reconnect];
}
@end
#endif

View File

@@ -0,0 +1,132 @@
//
// 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 <Foundation/Foundation.h>
#import <Security/SecCertificate.h>
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 <NSStreamDelegate>
@property (nonatomic, weak) id<RCTSRWebSocketDelegate> 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<NSString *> *)protocols NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithURLRequest:(NSURLRequest *)request;
// Some helper constructors.
- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)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 <NSObject>
// 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,559 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; };
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; };
13526A521F362F7F0008EF00 /* libfishhook.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13526A511F362F7F0008EF00 /* libfishhook.a */; };
2D3B5F3D1D9B165B00451313 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; };
2D3B5F3E1D9B165B00451313 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; };
2D3B5F401D9B165B00451313 /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; };
2DC5E5281F3A6CFD000EE84B /* libfishhook-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DC5E5271F3A6CFD000EE84B /* libfishhook-tvOS.a */; };
3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; };
3DBE0D141F3B185A0099AA32 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; };
3DBE0D151F3B185A0099AA32 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; };
3DBE0D801F3B1AF00099AA32 /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; };
3DBE0D821F3B1B0C0099AA32 /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; };
A12E9E2E1E5DEC4E0029001B /* RCTReconnectingWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */; };
A12E9E2F1E5DEC550029001B /* RCTReconnectingWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
3DBE0D0E1F3B18490099AA32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 3C86DF3E1ADF2C930047B81A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3DBE0CF41F3B181A0099AA32;
remoteInfo = fishhook;
};
3DBE0D101F3B184D0099AA32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 3C86DF3E1ADF2C930047B81A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3DBE0D011F3B181C0099AA32;
remoteInfo = "fishhook-tvOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
3DBE0D7F1F3B1AEC0099AA32 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/fishhook;
dstSubfolderSpec = 16;
files = (
3DBE0D801F3B1AF00099AA32 /* fishhook.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3DBE0D811F3B1B010099AA32 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/fishhook;
dstSubfolderSpec = 16;
files = (
3DBE0D821F3B1B0C0099AA32 /* fishhook.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1338BBDC1B04ACC80064A9C9 /* RCTSRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSRWebSocket.h; sourceTree = "<group>"; };
1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSRWebSocket.m; sourceTree = "<group>"; };
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = "<group>"; };
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = "<group>"; };
13526A511F362F7F0008EF00 /* libfishhook.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libfishhook.a; sourceTree = "<group>"; };
2D2A28881D9B049200D4039D /* libRCTWebSocket-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTWebSocket-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2DC5E5271F3A6CFD000EE84B /* libfishhook-tvOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libfishhook-tvOS.a"; path = "../fishhook/build/Debug-appletvos/libfishhook-tvOS.a"; sourceTree = "<group>"; };
3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketModule.h; sourceTree = "<group>"; };
3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketModule.m; sourceTree = "<group>"; tabWidth = 2; };
3DBE0D001F3B181A0099AA32 /* libfishhook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfishhook.a; sourceTree = BUILT_PRODUCTS_DIR; };
3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libfishhook-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3DBE0D121F3B185A0099AA32 /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fishhook.c; path = ../fishhook/fishhook.c; sourceTree = "<group>"; };
3DBE0D131F3B185A0099AA32 /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fishhook.h; path = ../fishhook/fishhook.h; sourceTree = "<group>"; };
A12E9E2C1E5DEC4E0029001B /* RCTReconnectingWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTReconnectingWebSocket.h; sourceTree = "<group>"; };
A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTReconnectingWebSocket.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
13526A4F1F362F770008EF00 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
13526A521F362F7F0008EF00 /* libfishhook.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2DC5E5151F3A6C39000EE84B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2DC5E5281F3A6CFD000EE84B /* libfishhook-tvOS.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
13526A501F362F7F0008EF00 /* Frameworks */ = {
isa = PBXGroup;
children = (
2DC5E5271F3A6CFD000EE84B /* libfishhook-tvOS.a */,
13526A511F362F7F0008EF00 /* libfishhook.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
3C86DF3D1ADF2C930047B81A = {
isa = PBXGroup;
children = (
3DBE0D121F3B185A0099AA32 /* fishhook.c */,
3DBE0D131F3B185A0099AA32 /* fishhook.h */,
A12E9E2C1E5DEC4E0029001B /* RCTReconnectingWebSocket.h */,
A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */,
1338BBDC1B04ACC80064A9C9 /* RCTSRWebSocket.h */,
1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */,
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */,
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */,
3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */,
3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */,
3C86DF471ADF2C930047B81A /* Products */,
13526A501F362F7F0008EF00 /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
3C86DF471ADF2C930047B81A /* Products */ = {
isa = PBXGroup;
children = (
3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */,
2D2A28881D9B049200D4039D /* libRCTWebSocket-tvOS.a */,
3DBE0D001F3B181A0099AA32 /* libfishhook.a */,
3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
2D2A28871D9B049200D4039D /* RCTWebSocket-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D2A28901D9B049200D4039D /* Build configuration list for PBXNativeTarget "RCTWebSocket-tvOS" */;
buildPhases = (
2D2A28841D9B049200D4039D /* Sources */,
2DC5E5151F3A6C39000EE84B /* Frameworks */,
);
buildRules = (
);
dependencies = (
3DBE0D111F3B184D0099AA32 /* PBXTargetDependency */,
);
name = "RCTWebSocket-tvOS";
productName = "RCTWebSocket-tvOS";
productReference = 2D2A28881D9B049200D4039D /* libRCTWebSocket-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
3C86DF451ADF2C930047B81A /* RCTWebSocket */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3C86DF5A1ADF2C930047B81A /* Build configuration list for PBXNativeTarget "RCTWebSocket" */;
buildPhases = (
3C86DF421ADF2C930047B81A /* Sources */,
13526A4F1F362F770008EF00 /* Frameworks */,
);
buildRules = (
);
dependencies = (
3DBE0D0F1F3B18490099AA32 /* PBXTargetDependency */,
);
name = RCTWebSocket;
productName = WebSocket;
productReference = 3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */;
productType = "com.apple.product-type.library.static";
};
3DBE0CF41F3B181A0099AA32 /* fishhook */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3DBE0CFD1F3B181A0099AA32 /* Build configuration list for PBXNativeTarget "fishhook" */;
buildPhases = (
3DBE0D7F1F3B1AEC0099AA32 /* CopyFiles */,
3DBE0CF51F3B181A0099AA32 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = fishhook;
productName = WebSocket;
productReference = 3DBE0D001F3B181A0099AA32 /* libfishhook.a */;
productType = "com.apple.product-type.library.static";
};
3DBE0D011F3B181C0099AA32 /* fishhook-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3DBE0D0A1F3B181C0099AA32 /* Build configuration list for PBXNativeTarget "fishhook-tvOS" */;
buildPhases = (
3DBE0D811F3B1B010099AA32 /* CopyFiles */,
3DBE0D021F3B181C0099AA32 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = "fishhook-tvOS";
productName = "RCTWebSocket-tvOS";
productReference = 3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
3C86DF3E1ADF2C930047B81A /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0630;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
2D2A28871D9B049200D4039D = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
3C86DF451ADF2C930047B81A = {
CreatedOnToolsVersion = 6.3;
};
};
};
buildConfigurationList = 3C86DF411ADF2C930047B81A /* Build configuration list for PBXProject "RCTWebSocket" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 3C86DF3D1ADF2C930047B81A;
productRefGroup = 3C86DF471ADF2C930047B81A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
3C86DF451ADF2C930047B81A /* RCTWebSocket */,
2D2A28871D9B049200D4039D /* RCTWebSocket-tvOS */,
3DBE0CF41F3B181A0099AA32 /* fishhook */,
3DBE0D011F3B181C0099AA32 /* fishhook-tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
2D2A28841D9B049200D4039D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D3B5F3E1D9B165B00451313 /* RCTWebSocketExecutor.m in Sources */,
2D3B5F401D9B165B00451313 /* RCTWebSocketModule.m in Sources */,
A12E9E2F1E5DEC550029001B /* RCTReconnectingWebSocket.m in Sources */,
2D3B5F3D1D9B165B00451313 /* RCTSRWebSocket.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3C86DF421ADF2C930047B81A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */,
3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */,
A12E9E2E1E5DEC4E0029001B /* RCTReconnectingWebSocket.m in Sources */,
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3DBE0CF51F3B181A0099AA32 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3DBE0D141F3B185A0099AA32 /* fishhook.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3DBE0D021F3B181C0099AA32 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3DBE0D151F3B185A0099AA32 /* fishhook.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
3DBE0D0F1F3B18490099AA32 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3DBE0CF41F3B181A0099AA32 /* fishhook */;
targetProxy = 3DBE0D0E1F3B18490099AA32 /* PBXContainerItemProxy */;
};
3DBE0D111F3B184D0099AA32 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3DBE0D011F3B181C0099AA32 /* fishhook-tvOS */;
targetProxy = 3DBE0D101F3B184D0099AA32 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
2D2A288E1D9B049200D4039D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
2D2A288F1D9B049200D4039D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
3C86DF581ADF2C930047B81A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Debug;
};
3C86DF591ADF2C930047B81A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Release;
};
3C86DF5B1ADF2C930047B81A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"RCT_METRO_PORT=${RCT_METRO_PORT}",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
3C86DF5C1ADF2C930047B81A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = "RCT_METRO_PORT=${RCT_METRO_PORT}";
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
3DBE0CFE1F3B181A0099AA32 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
3DBE0CFF1F3B181A0099AA32 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
3DBE0D0B1F3B181C0099AA32 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
3DBE0D0C1F3B181C0099AA32 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2D2A28901D9B049200D4039D /* Build configuration list for PBXNativeTarget "RCTWebSocket-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D2A288E1D9B049200D4039D /* Debug */,
2D2A288F1D9B049200D4039D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3C86DF411ADF2C930047B81A /* Build configuration list for PBXProject "RCTWebSocket" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3C86DF581ADF2C930047B81A /* Debug */,
3C86DF591ADF2C930047B81A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3C86DF5A1ADF2C930047B81A /* Build configuration list for PBXNativeTarget "RCTWebSocket" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3C86DF5B1ADF2C930047B81A /* Debug */,
3C86DF5C1ADF2C930047B81A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3DBE0CFD1F3B181A0099AA32 /* Build configuration list for PBXNativeTarget "fishhook" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3DBE0CFE1F3B181A0099AA32 /* Debug */,
3DBE0CFF1F3B181A0099AA32 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3DBE0D0A1F3B181C0099AA32 /* Build configuration list for PBXNativeTarget "fishhook-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3DBE0D0B1F3B181C0099AA32 /* Debug */,
3DBE0D0C1F3B181C0099AA32 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 3C86DF3E1ADF2C930047B81A /* Project object */;
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTDefines.h>
#import <React/RCTJavaScriptExecutor.h>
#if RCT_DEV // Debug executors are only supported in dev mode
@interface RCTWebSocketExecutor : NSObject <RCTJavaScriptExecutor>
- (instancetype)initWithURL:(NSURL *)URL;
@end
#endif

View File

@@ -0,0 +1,273 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTWebSocketExecutor.h"
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import "RCTSRWebSocket.h"
#if RCT_DEV // Debug executors are only supported in dev mode
typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary<NSString *, id> *reply);
@interface RCTWebSocketExecutor () <RCTSRWebSocketDelegate>
@end
@implementation RCTWebSocketExecutor
{
RCTSRWebSocket *_socket;
dispatch_queue_t _jsQueue;
NSMutableDictionary<NSNumber *, RCTWSMessageCallback> *_callbacks;
dispatch_semaphore_t _socketOpenSemaphore;
NSMutableDictionary<NSString *, NSString *> *_injectedObjects;
NSURL *_url;
NSError *_setupError;
}
RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
- (instancetype)initWithURL:(NSURL *)URL
{
RCTAssertParam(URL);
if ((self = [self init])) {
_url = URL;
}
return self;
}
- (void)setUp
{
if (!_url) {
NSInteger port = [[[_bridge bundleURL] port] integerValue] ?: RCT_METRO_PORT;
NSString *host = [[_bridge bundleURL] host] ?: @"localhost";
NSString *URLString = [NSString stringWithFormat:@"http://%@:%lld/debugger-proxy?role=client", host, (long long)port];
_url = [RCTConvert NSURL:URLString];
}
_jsQueue = dispatch_queue_create("com.facebook.react.WebSocketExecutor", DISPATCH_QUEUE_SERIAL);
_socket = [[RCTSRWebSocket alloc] initWithURL:_url];
_socket.delegate = self;
_callbacks = [NSMutableDictionary new];
_injectedObjects = [NSMutableDictionary new];
[_socket setDelegateDispatchQueue:_jsQueue];
NSURL *startDevToolsURL = [NSURL URLWithString:@"/launch-js-devtools" relativeToURL:_url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:startDevToolsURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){}];
[dataTask resume];
if (![self connectToProxy]) {
[self invalidate];
NSString *error = [NSString stringWithFormat:@"Connection to %@ timed out. Are you "
"running node proxy? If you are running on the device, check if "
"you have the right IP address in `RCTWebSocketExecutor.m`.", _url];
_setupError = RCTErrorWithMessage(error);
RCTFatal(_setupError);
return;
}
NSInteger retries = 3;
BOOL runtimeIsReady = [self prepareJSRuntime];
while (!runtimeIsReady && retries > 0) {
runtimeIsReady = [self prepareJSRuntime];
retries--;
}
if (!runtimeIsReady) {
[self invalidate];
NSString *error = @"Runtime is not ready for debugging.\n "
"- Make sure Packager server is running.\n"
"- Make sure the JavaScript Debugger is running and not paused on a "
"breakpoint or exception and try reloading again.";
_setupError = RCTErrorWithMessage(error);
RCTFatal(_setupError);
return;
}
}
- (BOOL)connectToProxy
{
_socketOpenSemaphore = dispatch_semaphore_create(0);
[_socket open];
long connected = dispatch_semaphore_wait(_socketOpenSemaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10));
return connected == 0 && _socket.readyState == RCTSR_OPEN;
}
- (BOOL)prepareJSRuntime
{
__block NSError *initError;
dispatch_semaphore_t s = dispatch_semaphore_create(0);
[self sendMessage:@{@"method": @"prepareJSRuntime"} onReply:^(NSError *error, NSDictionary<NSString *, id> *reply) {
initError = error;
dispatch_semaphore_signal(s);
}];
long runtimeIsReady = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5));
if (initError) {
RCTLogInfo(@"Websocket runtime setup failed: %@", initError);
}
return runtimeIsReady == 0 && initError == nil;
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{
NSError *error = nil;
NSDictionary<NSString *, id> *reply = RCTJSONParse(message, &error);
NSNumber *messageID = reply[@"replyID"];
RCTWSMessageCallback callback = _callbacks[messageID];
if (callback) {
callback(error, reply);
[_callbacks removeObjectForKey:messageID];
}
}
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
{
dispatch_semaphore_signal(_socketOpenSemaphore);
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
{
dispatch_semaphore_signal(_socketOpenSemaphore);
RCTLogInfo(@"WebSocket connection failed with error %@", error);
}
- (void)sendMessage:(NSDictionary<NSString *, id> *)message onReply:(RCTWSMessageCallback)callback
{
static NSUInteger lastID = 10000;
if (_setupError) {
callback(_setupError, nil);
return;
}
dispatch_async(_jsQueue, ^{
if (!self.valid) {
callback(RCTErrorWithMessage(@"Runtime is not ready for debugging. Make sure Packager server is running."), nil);
return;
}
NSNumber *expectedID = @(lastID++);
self->_callbacks[expectedID] = [callback copy];
NSMutableDictionary<NSString *, id> *messageWithID = [message mutableCopy];
messageWithID[@"id"] = expectedID;
[self->_socket send:RCTJSONStringify(messageWithID, NULL)];
});
}
- (void)executeApplicationScript:(NSData *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
// Hack: the bridge transitions out of loading state as soon as this method returns, which prevents us
// from completely invalidating the bridge and preventing an endless barage of RCTLog.logIfNoNativeHook
// calls if the JS execution environment is broken. We therefore block this thread until this message has returned.
dispatch_semaphore_t scriptSem = dispatch_semaphore_create(0);
NSDictionary<NSString *, id> *message = @{
@"method": @"executeApplicationScript",
@"url": RCTNullIfNil(URL.absoluteString),
@"inject": _injectedObjects,
};
[self sendMessage:message onReply:^(NSError *socketError, NSDictionary<NSString *, id> *reply) {
if (socketError) {
onComplete(socketError);
} else {
NSString *error = reply[@"error"];
onComplete(error ? RCTErrorWithMessage(error) : nil);
}
dispatch_semaphore_signal(scriptSem);
}];
dispatch_semaphore_wait(scriptSem, DISPATCH_TIME_FOREVER);
}
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
{
[self _executeJSCall:@"flushedQueue" arguments:@[] callback:onComplete];
}
- (void)callFunctionOnModule:(NSString *)module
method:(NSString *)method
arguments:(NSArray *)args
callback:(RCTJavaScriptCallback)onComplete
{
[self _executeJSCall:@"callFunctionReturnFlushedQueue" arguments:@[module, method, args] callback:onComplete];
}
- (void)invokeCallbackID:(NSNumber *)cbID
arguments:(NSArray *)args
callback:(RCTJavaScriptCallback)onComplete
{
[self _executeJSCall:@"invokeCallbackAndReturnFlushedQueue" arguments:@[cbID, args] callback:onComplete];
}
- (void)_executeJSCall:(NSString *)method arguments:(NSArray *)arguments callback:(RCTJavaScriptCallback)onComplete
{
RCTAssert(onComplete != nil, @"callback was missing for exec JS call");
NSDictionary<NSString *, id> *message = @{
@"method": method,
@"arguments": arguments
};
[self sendMessage:message onReply:^(NSError *socketError, NSDictionary<NSString *, id> *reply) {
if (socketError) {
onComplete(nil, socketError);
return;
}
NSError *jsonError;
id result = RCTJSONParse(reply[@"result"], &jsonError);
NSString *error = reply[@"error"];
onComplete(result, error ? RCTErrorWithMessage(error) : jsonError);
}];
}
- (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete
{
dispatch_async(_jsQueue, ^{
self->_injectedObjects[objectName] = script;
onComplete(nil);
});
}
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
RCTExecuteOnMainQueue(block);
}
- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block
{
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)invalidate
{
_socket.delegate = nil;
[_socket closeWithCode:1000 reason:@"Invalidated"];
_socket = nil;
}
- (BOOL)isValid
{
return _socket != nil && _socket.readyState == RCTSR_OPEN;
}
- (void)dealloc
{
RCTAssert(!self.valid, @"-invalidate must be called before -dealloc");
}
@end
#endif

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTEventEmitter.h>
NS_ASSUME_NONNULL_BEGIN
@protocol RCTWebSocketContentHandler <NSObject>
- (id)processWebsocketMessage:(id __nullable)message
forSocketID:(NSNumber *)socketID
withType:(NSString *__nonnull __autoreleasing *__nonnull)type;
@end
@interface RCTWebSocketModule : RCTEventEmitter
// Register a custom handler for a specific websocket. The handler will be strongly held by the WebSocketModule.
- (void)setContentHandler:(id<RCTWebSocketContentHandler> __nullable)handler forSocketID:(NSNumber *)socketID;
- (void)sendData:(NSData *)data forSocketID:(nonnull NSNumber *)socketID;
@end
@interface RCTBridge (RCTWebSocketModule)
- (RCTWebSocketModule *)webSocketModule;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,197 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTWebSocketModule.h"
#import <objc/runtime.h>
#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
#import "RCTSRWebSocket.h"
@implementation RCTSRWebSocket (React)
- (NSNumber *)reactTag
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setReactTag:(NSNumber *)reactTag
{
objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
@interface RCTWebSocketModule () <RCTSRWebSocketDelegate>
@end
@implementation RCTWebSocketModule
{
NSMutableDictionary<NSNumber *, RCTSRWebSocket *> *_sockets;
NSMutableDictionary<NSNumber *, id<RCTWebSocketContentHandler>> *_contentHandlers;
}
RCT_EXPORT_MODULE()
// Used by RCTBlobModule
@synthesize methodQueue = _methodQueue;
- (NSArray *)supportedEvents
{
return @[@"websocketMessage",
@"websocketOpen",
@"websocketFailed",
@"websocketClosed"];
}
- (void)invalidate
{
_contentHandlers = nil;
for (RCTSRWebSocket *socket in _sockets.allValues) {
socket.delegate = nil;
[socket close];
}
}
RCT_EXPORT_METHOD(connect:(NSURL *)URL protocols:(NSArray *)protocols options:(NSDictionary *)options socketID:(nonnull NSNumber *)socketID)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
// We load cookies from sharedHTTPCookieStorage (shared with XHR and
// fetch). To get secure cookies for wss URLs, replace wss with https
// in the URL.
NSURLComponents *components = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:true];
if ([components.scheme.lowercaseString isEqualToString:@"wss"]) {
components.scheme = @"https";
}
// Load and set the cookie header.
NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:components.URL];
request.allHTTPHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
// Load supplied headers
[options[@"headers"] enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
[request addValue:[RCTConvert NSString:value] forHTTPHeaderField:key];
}];
RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols];
webSocket.delegate = self;
webSocket.reactTag = socketID;
if (!_sockets) {
_sockets = [NSMutableDictionary new];
}
_sockets[socketID] = webSocket;
[webSocket open];
}
RCT_EXPORT_METHOD(send:(NSString *)message forSocketID:(nonnull NSNumber *)socketID)
{
[_sockets[socketID] send:message];
}
RCT_EXPORT_METHOD(sendBinary:(NSString *)base64String forSocketID:(nonnull NSNumber *)socketID)
{
[self sendData:[[NSData alloc] initWithBase64EncodedString:base64String options:0] forSocketID:socketID];
}
- (void)sendData:(NSData *)data forSocketID:(nonnull NSNumber *)socketID
{
[_sockets[socketID] send:data];
}
RCT_EXPORT_METHOD(ping:(nonnull NSNumber *)socketID)
{
[_sockets[socketID] sendPing:NULL];
}
RCT_EXPORT_METHOD(close:(nonnull NSNumber *)socketID)
{
[_sockets[socketID] close];
[_sockets removeObjectForKey:socketID];
}
- (void)setContentHandler:(id<RCTWebSocketContentHandler>)handler forSocketID:(NSString *)socketID
{
if (!_contentHandlers) {
_contentHandlers = [NSMutableDictionary new];
}
_contentHandlers[socketID] = handler;
}
#pragma mark - RCTSRWebSocketDelegate methods
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{
NSString *type;
NSNumber *socketID = [webSocket reactTag];
id contentHandler = _contentHandlers[socketID];
if (contentHandler) {
message = [contentHandler processWebsocketMessage:message forSocketID:socketID withType:&type];
} else {
if ([message isKindOfClass:[NSData class]]) {
type = @"binary";
message = [message base64EncodedStringWithOptions:0];
} else {
type = @"text";
}
}
[self sendEventWithName:@"websocketMessage" body:@{
@"data": message,
@"type": type,
@"id": webSocket.reactTag
}];
}
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
{
[self sendEventWithName:@"websocketOpen" body:@{
@"id": webSocket.reactTag
}];
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
{
NSNumber *socketID = [webSocket reactTag];
_contentHandlers[socketID] = nil;
_sockets[socketID] = nil;
[self sendEventWithName:@"websocketFailed" body:@{
@"message": error.localizedDescription,
@"id": socketID
}];
}
- (void)webSocket:(RCTSRWebSocket *)webSocket
didCloseWithCode:(NSInteger)code
reason:(NSString *)reason
wasClean:(BOOL)wasClean
{
NSNumber *socketID = [webSocket reactTag];
_contentHandlers[socketID] = nil;
_sockets[socketID] = nil;
[self sendEventWithName:@"websocketClosed" body:@{
@"code": @(code),
@"reason": RCTNullIfNil(reason),
@"clean": @(wasClean),
@"id": socketID
}];
}
@end
@implementation RCTBridge (RCTWebSocketModule)
- (RCTWebSocketModule *)webSocketModule
{
return [self moduleForClass:[RCTWebSocketModule class]];
}
@end

View File

@@ -0,0 +1,279 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule WebSocket
* @flow
*/
'use strict';
const Blob = require('Blob');
const EventTarget = require('event-target-shim');
const NativeEventEmitter = require('NativeEventEmitter');
const BlobManager = require('BlobManager');
const NativeModules = require('NativeModules');
const Platform = require('Platform');
const WebSocketEvent = require('WebSocketEvent');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
const base64 = require('base64-js');
const binaryToBase64 = require('binaryToBase64');
const invariant = require('fbjs/lib/invariant');
const {WebSocketModule} = NativeModules;
import type EventSubscription from 'EventSubscription';
type ArrayBufferView =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| DataView
type BinaryType = 'blob' | 'arraybuffer'
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;
const CLOSE_NORMAL = 1000;
const WEBSOCKET_EVENTS = [
'close',
'error',
'message',
'open',
];
let nextWebSocketId = 0;
/**
* Browser-compatible WebSockets implementation.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
* See https://github.com/websockets/ws
*/
class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
static CONNECTING = CONNECTING;
static OPEN = OPEN;
static CLOSING = CLOSING;
static CLOSED = CLOSED;
CONNECTING: number = CONNECTING;
OPEN: number = OPEN;
CLOSING: number = CLOSING;
CLOSED: number = CLOSED;
_socketId: number;
_eventEmitter: NativeEventEmitter;
_subscriptions: Array<EventSubscription>;
_binaryType: ?BinaryType;
onclose: ?Function;
onerror: ?Function;
onmessage: ?Function;
onopen: ?Function;
bufferedAmount: number;
extension: ?string;
protocol: ?string;
readyState: number = CONNECTING;
url: ?string;
// This module depends on the native `WebSocketModule` module. If you don't include it,
// `WebSocket.isAvailable` will return `false`, and WebSocket constructor will throw an error
static isAvailable: boolean = !!WebSocketModule;
constructor(url: string, protocols: ?string | ?Array<string>, options: ?{headers?: {origin?: string}}) {
super();
if (typeof protocols === 'string') {
protocols = [protocols];
}
const {headers = {}, ...unrecognized} = options || {};
// Preserve deprecated backwards compatibility for the 'origin' option
if (unrecognized && typeof unrecognized.origin === 'string') {
console.warn('Specifying `origin` as a WebSocket connection option is deprecated. Include it under `headers` instead.');
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed. To see
* the error delete this comment and run Flow. */
headers.origin = unrecognized.origin;
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed. To see
* the error delete this comment and run Flow. */
delete unrecognized.origin;
}
// Warn about and discard anything else
if (Object.keys(unrecognized).length > 0) {
console.warn('Unrecognized WebSocket connection option(s) `' + Object.keys(unrecognized).join('`, `') + '`. '
+ 'Did you mean to put these under `headers`?');
}
if (!Array.isArray(protocols)) {
protocols = null;
}
if (!WebSocket.isAvailable) {
throw new Error('Cannot initialize WebSocket module. ' +
'Native module WebSocketModule is missing.');
}
this._eventEmitter = new NativeEventEmitter(WebSocketModule);
this._socketId = nextWebSocketId++;
this._registerEvents();
WebSocketModule.connect(url, protocols, { headers }, this._socketId);
}
get binaryType(): ?BinaryType {
return this._binaryType;
}
set binaryType(binaryType: BinaryType): void {
if (binaryType !== 'blob' && binaryType !== 'arraybuffer') {
throw new Error('binaryType must be either \'blob\' or \'arraybuffer\'');
}
if (this._binaryType === 'blob' || binaryType === 'blob') {
invariant(BlobManager.isAvailable, 'Native module BlobModule is required for blob support');
if (binaryType === 'blob') {
BlobManager.addWebSocketHandler(this._socketId);
} else {
BlobManager.removeWebSocketHandler(this._socketId);
}
}
this._binaryType = binaryType;
}
get binaryType(): ?BinaryType {
return this._binaryType;
}
close(code?: number, reason?: string): void {
if (this.readyState === this.CLOSING ||
this.readyState === this.CLOSED) {
return;
}
this.readyState = this.CLOSING;
this._close(code, reason);
}
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {
if (this.readyState === this.CONNECTING) {
throw new Error('INVALID_STATE_ERR');
}
if (data instanceof Blob) {
invariant(BlobManager.isAvailable, 'Native module BlobModule is required for blob support');
BlobManager.sendOverSocket(data, this._socketId);
return;
}
if (typeof data === 'string') {
WebSocketModule.send(data, this._socketId);
return;
}
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
WebSocketModule.sendBinary(binaryToBase64(data), this._socketId);
return;
}
throw new Error('Unsupported data type');
}
ping(): void {
if (this.readyState === this.CONNECTING) {
throw new Error('INVALID_STATE_ERR');
}
WebSocketModule.ping(this._socketId);
}
_close(code?: number, reason?: string): void {
if (Platform.OS === 'android') {
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
const statusCode = typeof code === 'number' ? code : CLOSE_NORMAL;
const closeReason = typeof reason === 'string' ? reason : '';
WebSocketModule.close(statusCode, closeReason, this._socketId);
} else {
WebSocketModule.close(this._socketId);
}
if (BlobManager.isAvailable && this._binaryType === 'blob') {
BlobManager.removeWebSocketHandler(this._socketId);
}
}
_unregisterEvents(): void {
this._subscriptions.forEach(e => e.remove());
this._subscriptions = [];
}
_registerEvents(): void {
this._subscriptions = [
this._eventEmitter.addListener('websocketMessage', ev => {
if (ev.id !== this._socketId) {
return;
}
let data = ev.data;
switch (ev.type) {
case 'binary':
data = base64.toByteArray(ev.data).buffer;
break;
case 'blob':
data = BlobManager.createFromOptions(ev.data);
break;
}
this.dispatchEvent(new WebSocketEvent('message', { data }));
}),
this._eventEmitter.addListener('websocketOpen', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.OPEN;
this.dispatchEvent(new WebSocketEvent('open'));
}),
this._eventEmitter.addListener('websocketClosed', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.CLOSED;
this.dispatchEvent(new WebSocketEvent('close', {
code: ev.code,
reason: ev.reason,
}));
this._unregisterEvents();
this.close();
}),
this._eventEmitter.addListener('websocketFailed', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.CLOSED;
this.dispatchEvent(new WebSocketEvent('error', {
message: ev.message,
}));
this.dispatchEvent(new WebSocketEvent('close', {
message: ev.message,
}));
this._unregisterEvents();
this.close();
})
];
}
}
module.exports = WebSocket;

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule WebSocketEvent
*/
'use strict';
/**
* Event object passed to the `onopen`, `onclose`, `onmessage`, `onerror`
* callbacks of `WebSocket`.
*
* The `type` property is "open", "close", "message", "error" respectively.
*
* In case of "message", the `data` property contains the incoming data.
*/
class WebSocketEvent {
constructor(type, eventInitDict) {
this.type = type.toString();
Object.assign(this, eventInitDict);
}
}
module.exports = WebSocketEvent;

View File

@@ -0,0 +1,218 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule WebSocketInterceptor
*/
'use strict';
const RCTWebSocketModule = require('NativeModules').WebSocketModule;
const NativeEventEmitter = require('NativeEventEmitter');
const base64 = require('base64-js');
const originalRCTWebSocketConnect = RCTWebSocketModule.connect;
const originalRCTWebSocketSend = RCTWebSocketModule.send;
const originalRCTWebSocketSendBinary = RCTWebSocketModule.sendBinary;
const originalRCTWebSocketClose = RCTWebSocketModule.close;
let eventEmitter: NativeEventEmitter;
let subscriptions: Array<EventSubscription>;
let closeCallback;
let sendCallback;
let connectCallback;
let onOpenCallback;
let onMessageCallback;
let onErrorCallback;
let onCloseCallback;
let isInterceptorEnabled = false;
/**
* A network interceptor which monkey-patches RCTWebSocketModule methods
* to gather all websocket network requests/responses, in order to show
* their information in the React Native inspector development tool.
*/
const WebSocketInterceptor = {
/**
* Invoked when RCTWebSocketModule.close(...) is called.
*/
setCloseCallback(callback) {
closeCallback = callback;
},
/**
* Invoked when RCTWebSocketModule.send(...) or sendBinary(...) is called.
*/
setSendCallback(callback) {
sendCallback = callback;
},
/**
* Invoked when RCTWebSocketModule.connect(...) is called.
*/
setConnectCallback(callback) {
connectCallback = callback;
},
/**
* Invoked when event "websocketOpen" happens.
*/
setOnOpenCallback(callback) {
onOpenCallback = callback;
},
/**
* Invoked when event "websocketMessage" happens.
*/
setOnMessageCallback(callback) {
onMessageCallback = callback;
},
/**
* Invoked when event "websocketFailed" happens.
*/
setOnErrorCallback(callback) {
onErrorCallback = callback;
},
/**
* Invoked when event "websocketClosed" happens.
*/
setOnCloseCallback(callback) {
onCloseCallback = callback;
},
isInterceptorEnabled() {
return isInterceptorEnabled;
},
_unregisterEvents() {
subscriptions.forEach(e => e.remove());
subscriptions = [];
},
/**
* Add listeners to the RCTWebSocketModule events to intercept them.
*/
_registerEvents() {
subscriptions = [
eventEmitter.addListener('websocketMessage', ev => {
if (onMessageCallback) {
onMessageCallback(
ev.id,
(ev.type === 'binary') ?
WebSocketInterceptor._arrayBufferToString(ev.data) : ev.data,
);
}
}),
eventEmitter.addListener('websocketOpen', ev => {
if (onOpenCallback) {
onOpenCallback(ev.id);
}
}),
eventEmitter.addListener('websocketClosed', ev => {
if (onCloseCallback) {
onCloseCallback(ev.id, {code: ev.code, reason: ev.reason});
}
}),
eventEmitter.addListener('websocketFailed', ev => {
if (onErrorCallback) {
onErrorCallback(ev.id, {message: ev.message});
}
})
];
},
enableInterception() {
if (isInterceptorEnabled) {
return;
}
eventEmitter = new NativeEventEmitter(RCTWebSocketModule);
WebSocketInterceptor._registerEvents();
// Override `connect` method for all RCTWebSocketModule requests
// to intercept the request url, protocols, options and socketId,
// then pass them through the `connectCallback`.
RCTWebSocketModule.connect = function(url, protocols, options, socketId) {
if (connectCallback) {
connectCallback(url, protocols, options, socketId);
}
originalRCTWebSocketConnect.apply(this, arguments);
};
// Override `send` method for all RCTWebSocketModule requests to intercept
// the data sent, then pass them through the `sendCallback`.
RCTWebSocketModule.send = function(data, socketId) {
if (sendCallback) {
sendCallback(data, socketId);
}
originalRCTWebSocketSend.apply(this, arguments);
};
// Override `sendBinary` method for all RCTWebSocketModule requests to
// intercept the data sent, then pass them through the `sendCallback`.
RCTWebSocketModule.sendBinary = function(data, socketId) {
if (sendCallback) {
sendCallback(WebSocketInterceptor._arrayBufferToString(data), socketId);
}
originalRCTWebSocketSendBinary.apply(this, arguments);
};
// Override `close` method for all RCTWebSocketModule requests to intercept
// the close information, then pass them through the `closeCallback`.
RCTWebSocketModule.close = function() {
if (closeCallback) {
if (arguments.length === 3) {
closeCallback(arguments[0], arguments[1], arguments[2]);
} else {
closeCallback(null, null, arguments[0]);
}
}
originalRCTWebSocketClose.apply(this, arguments);
};
isInterceptorEnabled = true;
},
_arrayBufferToString(data) {
const value = base64.toByteArray(data).buffer;
if (value === undefined || value === null) {
return '(no value)';
}
if (typeof ArrayBuffer !== 'undefined' &&
typeof Uint8Array !== 'undefined' &&
value instanceof ArrayBuffer) {
return `ArrayBuffer {${String(Array.from(new Uint8Array(value)))}}`;
}
return value;
},
// Unpatch RCTWebSocketModule methods and remove the callbacks.
disableInterception() {
if (!isInterceptorEnabled) {
return;
}
isInterceptorEnabled = false;
RCTWebSocketModule.send = originalRCTWebSocketSend;
RCTWebSocketModule.sendBinary = originalRCTWebSocketSendBinary;
RCTWebSocketModule.close = originalRCTWebSocketClose;
RCTWebSocketModule.connect = originalRCTWebSocketConnect;
connectCallback = null;
closeCallback = null;
sendCallback = null;
onOpenCallback = null;
onMessageCallback = null;
onCloseCallback = null;
onErrorCallback = null;
WebSocketInterceptor._unregisterEvents();
},
};
module.exports = WebSocketInterceptor;

View File

@@ -0,0 +1,18 @@
// Jest fatals for the following statement (minimal repro case)
//
// exports.something = Symbol;
//
// Until it is fixed, mocking the entire node module makes the
// problem go away.
'use strict';
function EventTarget() {
// Support both EventTarget and EventTarget(...)
// as a super class, just like the original module does.
if (arguments.length > 0) {
return EventTarget;
}
}
module.exports = EventTarget;