Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 104 additions & 28 deletions packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,27 @@

#if RCT_DEV // Only supported in dev mode

static const NSTimeInterval RCTReconnectInitialDelay = 2.0;
static const NSTimeInterval RCTReconnectMaxDelay = 120.0;

@interface RCTReconnectingWebSocket () <SRWebSocketDelegate>
@end

@implementation RCTReconnectingWebSocket {
NSURL *_url;
SRWebSocket *_socket;
BOOL _stopped;
BOOL _reconnectPending;
NSTimeInterval _reconnectDelay;
uint64_t _reconnectToken;
}

- (instancetype)initWithURL:(NSURL *)url queue:(dispatch_queue_t)queue
{
if (self = [super init]) {
_url = url;
_delegateDispatchQueue = queue;
_reconnectDelay = RCTReconnectInitialDelay;
}
return self;
}
Expand All @@ -40,69 +47,138 @@ - (instancetype)initWithURL:(NSURL *)url

- (void)send:(id)data
{
[_socket sendData:data error:nil];
SRWebSocket *socket;
@synchronized (self) {
socket = _socket;
}
[socket sendData:data error:nil];
}

- (void)start
- (void)connect
{
[self stop];
_stopped = NO;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
[[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request];
_socket = [[SRWebSocket alloc] initWithURLRequest:request];
_socket.delegate = self;
[_socket setDelegateDispatchQueue:_delegateDispatchQueue];
[_socket open];
SRWebSocket *socketToClose = nil;
NSMutableURLRequest *request = nil;
SRWebSocket *socket = nil;

@synchronized (self) {
if (_stopped) {
return;
}
_reconnectPending = NO;

socketToClose = _socket;
_socket.delegate = nil;
_socket = nil;

request = [NSMutableURLRequest requestWithURL:_url];
[[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request];
socket = [[SRWebSocket alloc] initWithURLRequest:request];
socket.delegate = self;
[socket setDelegateDispatchQueue:_delegateDispatchQueue];
_socket = socket;
}

[socketToClose closeWithCode:1000 reason:@"Invalidated"];
[socket open];
}

- (void)stop
- (void)start
{
_stopped = YES;
_socket.delegate = nil;
[_socket closeWithCode:1000 reason:@"Invalidated"];
_socket = nil;
@synchronized (self) {
_stopped = NO;
_reconnectPending = NO;
_reconnectToken++;
_reconnectDelay = RCTReconnectInitialDelay;
}
[self connect];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
- (void)stop
{
[_delegate reconnectingWebSocket:self didReceiveMessage:message];
SRWebSocket *socketToClose = nil;
@synchronized (self) {
_stopped = YES;
_reconnectPending = NO;
_reconnectToken++;
_reconnectDelay = RCTReconnectInitialDelay;
socketToClose = _socket;
_socket.delegate = nil;
_socket = nil;
}
[socketToClose closeWithCode:1000 reason:@"Invalidated"];
}

- (void)reconnect
{
if (_stopped) {
return;
NSTimeInterval delay;
uint64_t token;
@synchronized (self) {
if (_stopped || _reconnectPending) {
return;
}
_reconnectPending = YES;
token = ++_reconnectToken;
delay = _reconnectDelay;
_reconnectDelay = MIN(_reconnectDelay * 2, RCTReconnectMaxDelay);
}

__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(), ^{
[weakSelf start];
if (!socket) {
[weakSelf reconnect];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
@synchronized (strongSelf) {
if (strongSelf->_stopped || strongSelf->_reconnectToken != token) {
return;
}
}
[strongSelf connect];
});
}

- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{
@synchronized (self) {
if (webSocket != _socket) {
return;
}
_reconnectDelay = RCTReconnectInitialDelay;
}
[_delegate reconnectingWebSocketDidOpen:self];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
@synchronized (self) {
if (webSocket != _socket) {
return;
}
}
[_delegate reconnectingWebSocket:self didReceiveMessage:message];
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
[_delegate reconnectingWebSocketDidClose:self];
if ([error code] != ECONNREFUSED) {
[self reconnect];
@synchronized (self) {
if (webSocket != _socket) {
return;
}
}
[_delegate reconnectingWebSocketDidClose:self];
[self reconnect];
}

- (void)webSocket:(SRWebSocket *)webSocket
didCloseWithCode:(NSInteger)code
reason:(NSString *)reason
wasClean:(BOOL)wasClean
{
@synchronized (self) {
if (webSocket != _socket) {
return;
}
}
[_delegate reconnectingWebSocketDidClose:self];
[self reconnect];
}
Expand Down
Loading