280 lines
9.1 KiB
Objective-C
280 lines
9.1 KiB
Objective-C
#import "StatusBackendClient.h"
|
|
#import "RCTStatus.h"
|
|
#import "Utils.h"
|
|
|
|
@implementation StatusBackendClient {
|
|
NSURLSessionWebSocketTask *_webSocket;
|
|
BOOL _serverEnabled;
|
|
NSString *_statusGoEndpoint;
|
|
NSString *_signalEndpoint;
|
|
NSString *_rootDataDir;
|
|
NSTimer *_pingTimer;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE();
|
|
|
|
+ (BOOL)requiresMainQueueSetup {
|
|
return YES;
|
|
}
|
|
|
|
+ (instancetype)allocWithZone:(NSZone *)zone {
|
|
static StatusBackendClient *sharedInstance = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
sharedInstance = [super allocWithZone:zone];
|
|
});
|
|
return sharedInstance;
|
|
}
|
|
|
|
+ (instancetype)sharedInstance {
|
|
return [[self alloc] init];
|
|
}
|
|
|
|
- (instancetype)init {
|
|
static StatusBackendClient *sharedInstance = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
sharedInstance = [super init];
|
|
if (sharedInstance) {
|
|
sharedInstance->_serverEnabled = NO;
|
|
sharedInstance->_statusGoEndpoint = nil;
|
|
sharedInstance->_signalEndpoint = nil;
|
|
sharedInstance->_rootDataDir = nil;
|
|
sharedInstance->_webSocket = nil;
|
|
}
|
|
});
|
|
return sharedInstance;
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)zone {
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)serverEnabled {
|
|
return _serverEnabled;
|
|
}
|
|
|
|
- (NSString *)statusGoEndpoint {
|
|
return _statusGoEndpoint;
|
|
}
|
|
|
|
- (NSString *)signalEndpoint {
|
|
return _signalEndpoint;
|
|
}
|
|
|
|
- (NSString *)rootDataDir {
|
|
return _rootDataDir;
|
|
}
|
|
|
|
- (void)connectWebSocket {
|
|
if (!self.serverEnabled || !self.signalEndpoint) {
|
|
return;
|
|
}
|
|
|
|
NSString *fullUrl = [NSString stringWithFormat:@"%@", self.signalEndpoint];
|
|
NSURL *url = [NSURL URLWithString:fullUrl];
|
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
|
|
self.webSocket = [session webSocketTaskWithURL:url];
|
|
|
|
[self.webSocket resume];
|
|
[self receiveMessage];
|
|
|
|
[self startPingTimer];
|
|
}
|
|
|
|
- (void)receiveMessage {
|
|
__weak typeof(self) weakSelf = self;
|
|
[self.webSocket receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage *message, NSError *error) {
|
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
if (!strongSelf || !strongSelf.webSocket) {
|
|
return;
|
|
}
|
|
|
|
if (error) {
|
|
NSLog(@"WebSocket error: %@", error);
|
|
// Attempt to reconnect after error
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
[strongSelf connectWebSocket];
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (message) {
|
|
if (message.type == NSURLSessionWebSocketMessageTypeString && message.string) {
|
|
[[Status sharedInstance] handleSignal:message.string];
|
|
}
|
|
// Continue receiving messages only if the connection is still active
|
|
if (strongSelf.webSocket) {
|
|
[strongSelf receiveMessage];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)disconnectWebSocket {
|
|
[self stopPingTimer];
|
|
if (self.webSocket) {
|
|
[self.webSocket cancel];
|
|
self.webSocket = nil;
|
|
}
|
|
}
|
|
|
|
- (void)request:(NSString *)endpoint
|
|
body:(NSString *)body
|
|
callback:(void (^)(NSString *response, NSError *error))callback {
|
|
NSString *fullUrlString = [NSString stringWithFormat:@"%@%@", self.statusGoEndpoint, endpoint];
|
|
NSLog(@"StatusGo server is enabled, executing request, endpoint: %@, body: %@", fullUrlString, body);
|
|
NSURL *url = [NSURL URLWithString:fullUrlString];
|
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
|
|
request.HTTPMethod = @"POST";
|
|
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
|
|
|
|
NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
|
|
request.HTTPBody = bodyData;
|
|
|
|
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
|
|
|
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
|
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
if (error) {
|
|
NSLog(@"request error: %@", error);
|
|
callback(nil, error);
|
|
return;
|
|
}
|
|
|
|
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
callback(responseString, nil);
|
|
}];
|
|
|
|
[task resume];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(configStatusBackendServer:(BOOL)serverEnabled
|
|
statusGoEndpoint:(NSString *)statusGoEndpoint
|
|
signalEndpoint:(NSString *)signalEndpoint
|
|
rootDataDir:(NSString *)rootDataDir) {
|
|
[self configureWithEnabled:serverEnabled
|
|
statusGoEndpoint:statusGoEndpoint
|
|
signalEndpoint:signalEndpoint
|
|
rootDataDir:rootDataDir];
|
|
}
|
|
|
|
- (void)configureWithEnabled:(BOOL)serverEnabled
|
|
statusGoEndpoint:(NSString *)statusGoEndpoint
|
|
signalEndpoint:(NSString *)signalEndpoint
|
|
rootDataDir:(NSString *)rootDataDir {
|
|
_serverEnabled = serverEnabled;
|
|
|
|
if (serverEnabled) {
|
|
_statusGoEndpoint = statusGoEndpoint;
|
|
_signalEndpoint = signalEndpoint;
|
|
_rootDataDir = rootDataDir;
|
|
[self connectWebSocket];
|
|
} else {
|
|
[self disconnectWebSocket];
|
|
_statusGoEndpoint = nil;
|
|
_signalEndpoint = nil;
|
|
_rootDataDir = nil;
|
|
}
|
|
}
|
|
|
|
+ (void)executeStatusGoRequest:(NSString *)endpoint
|
|
body:(NSString *)body
|
|
statusgoFunction:(NSString * (^)(void))statusgoFunction {
|
|
StatusBackendClient *client = [StatusBackendClient sharedInstance];
|
|
if (client.serverEnabled) {
|
|
[client request:endpoint body:body callback:^(NSString *response, NSError *error) {
|
|
[Utils handleStatusGoResponse:response source:endpoint error:error];
|
|
}];
|
|
} else {
|
|
NSString *result = statusgoFunction();
|
|
[Utils handleStatusGoResponse:result source:endpoint error:nil];
|
|
}
|
|
}
|
|
|
|
+ (void)executeStatusGoRequestWithCallback:(NSString *)endpoint
|
|
body:(NSString *)body
|
|
statusgoFunction:(NSString * (^)(void))statusgoFunction
|
|
callback:(RCTResponseSenderBlock)callback {
|
|
StatusBackendClient *client = [StatusBackendClient sharedInstance];
|
|
if (client.serverEnabled) {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
[client request:endpoint body:body callback:^(NSString *response, NSError *error) {
|
|
if (error) {
|
|
NSLog(@"request to %@ failed: %@", endpoint, error);
|
|
if (callback) {
|
|
callback(@[@(NO)]);
|
|
}
|
|
} else {
|
|
if (callback) {
|
|
callback(@[response]);
|
|
}
|
|
}
|
|
}];
|
|
});
|
|
} else {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSString *result = statusgoFunction();
|
|
if (callback) {
|
|
callback(@[result]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
+ (NSString *)executeStatusGoRequestWithResult:(NSString *)endpoint
|
|
body:(NSString *)body
|
|
statusgoFunction:(NSString * (^)(void))statusgoFunction {
|
|
StatusBackendClient *client = [StatusBackendClient sharedInstance];
|
|
if (client.serverEnabled) {
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
__block NSString *resultString = @"";
|
|
[client request:endpoint body:body callback:^(NSString *response, NSError *error) {
|
|
if (error) {
|
|
NSLog(@"request to %@ failed: %@", endpoint, error);
|
|
resultString = @"";
|
|
} else {
|
|
resultString = response;
|
|
}
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
|
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
return resultString;
|
|
} else {
|
|
return statusgoFunction();
|
|
}
|
|
}
|
|
|
|
- (void)startPingTimer {
|
|
[self stopPingTimer];
|
|
_pingTimer = [NSTimer scheduledTimerWithTimeInterval:30.0
|
|
target:self
|
|
selector:@selector(sendPing)
|
|
userInfo:nil
|
|
repeats:YES];
|
|
}
|
|
|
|
- (void)stopPingTimer {
|
|
if (_pingTimer) {
|
|
[_pingTimer invalidate];
|
|
_pingTimer = nil;
|
|
}
|
|
}
|
|
|
|
- (void)sendPing {
|
|
if (self.webSocket) {
|
|
[self.webSocket sendPingWithPongReceiveHandler:^(NSError * _Nullable error) {
|
|
if (error) {
|
|
NSLog(@"WebSocket ping failed: %@", error);
|
|
// Reconnect on ping failure
|
|
[self disconnectWebSocket];
|
|
[self connectWebSocket];
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
@end
|