|
@@ -236,6 +236,7 @@ typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
|
|
|
|
|
|
BOOL _sentClose;
|
|
BOOL _sentClose;
|
|
BOOL _didFail;
|
|
BOOL _didFail;
|
|
|
|
+ BOOL _cleanupScheduled;
|
|
int _closeCode;
|
|
int _closeCode;
|
|
|
|
|
|
BOOL _isPumping;
|
|
BOOL _isPumping;
|
|
@@ -762,11 +763,11 @@ static __strong NSData *CRLFCRLF;
|
|
}];
|
|
}];
|
|
|
|
|
|
self.readyState = SR_CLOSED;
|
|
self.readyState = SR_CLOSED;
|
|
- _selfRetain = nil;
|
|
|
|
|
|
|
|
SRFastLog(@"Failing with error %@", error.localizedDescription);
|
|
SRFastLog(@"Failing with error %@", error.localizedDescription);
|
|
|
|
|
|
[self closeConnection];
|
|
[self closeConnection];
|
|
|
|
+ [self _scheduleCleanup];
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -1174,13 +1175,15 @@ static const uint8_t SRPayloadLenMask = 0x7F;
|
|
_inputStream.streamStatus != NSStreamStatusClosed) &&
|
|
_inputStream.streamStatus != NSStreamStatusClosed) &&
|
|
!_sentClose) {
|
|
!_sentClose) {
|
|
_sentClose = YES;
|
|
_sentClose = YES;
|
|
-
|
|
|
|
- [_outputStream close];
|
|
|
|
- [_inputStream close];
|
|
|
|
|
|
|
|
-
|
|
|
|
- for (NSArray *runLoop in [_scheduledRunloops copy]) {
|
|
|
|
- [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]];
|
|
|
|
|
|
+ @synchronized(self) {
|
|
|
|
+ [_outputStream close];
|
|
|
|
+ [_inputStream close];
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (NSArray *runLoop in [_scheduledRunloops copy]) {
|
|
|
|
+ [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]];
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (!_failed) {
|
|
if (!_failed) {
|
|
@@ -1191,7 +1194,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
|
|
}];
|
|
}];
|
|
}
|
|
}
|
|
|
|
|
|
- _selfRetain = nil;
|
|
|
|
|
|
+ [self _scheduleCleanup];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1218,6 +1221,41 @@ static const uint8_t SRPayloadLenMask = 0x7F;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+- (void)_scheduleCleanup
|
|
|
|
+{
|
|
|
|
+ @synchronized(self) {
|
|
|
|
+ if (_cleanupScheduled) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _cleanupScheduled = YES;
|
|
|
|
+
|
|
|
|
+ // Cleanup NSStream delegate's 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 SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)_cleanupSelfReference:(NSTimer *)timer
|
|
|
|
+{
|
|
|
|
+ @synchronized(self) {
|
|
|
|
+ // Nuke NSStream delegate's
|
|
|
|
+ _inputStream.delegate = nil;
|
|
|
|
+ _outputStream.delegate = nil;
|
|
|
|
+
|
|
|
|
+ // Remove the streams, right now, from the networkRunLoop
|
|
|
|
+ [_inputStream close];
|
|
|
|
+ [_outputStream close];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Cleanup selfRetain in the same GCD queue as usual
|
|
|
|
+ dispatch_async(_workQueue, ^{
|
|
|
|
+ _selfRetain = nil;
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
|
|
static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
|
|
|
|
|
|
- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
|
|
- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
|
|
@@ -1459,6 +1497,8 @@ static const size_t SRFrameHeaderOverhead = 32;
|
|
|
|
|
|
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
|
|
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
|
|
{
|
|
{
|
|
|
|
+ __weak typeof(self) weakSelf = self;
|
|
|
|
+
|
|
if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {
|
|
if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {
|
|
|
|
|
|
NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
|
|
NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
|
|
@@ -1484,7 +1524,8 @@ static const size_t SRFrameHeaderOverhead = 32;
|
|
|
|
|
|
if (!_pinnedCertFound) {
|
|
if (!_pinnedCertFound) {
|
|
dispatch_async(_workQueue, ^{
|
|
dispatch_async(_workQueue, ^{
|
|
- [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]];
|
|
|
|
|
|
+ NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" };
|
|
|
|
+ [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]];
|
|
});
|
|
});
|
|
return;
|
|
return;
|
|
} else if (aStream == _outputStream) {
|
|
} else if (aStream == _outputStream) {
|
|
@@ -1496,6 +1537,12 @@ static const size_t SRFrameHeaderOverhead = 32;
|
|
}
|
|
}
|
|
|
|
|
|
dispatch_async(_workQueue, ^{
|
|
dispatch_async(_workQueue, ^{
|
|
|
|
+ [weakSelf safeHandleEvent:eventCode stream:aStream];
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream
|
|
|
|
+{
|
|
switch (eventCode) {
|
|
switch (eventCode) {
|
|
case NSStreamEventOpenCompleted: {
|
|
case NSStreamEventOpenCompleted: {
|
|
SRFastLog(@"NSStreamEventOpenCompleted %@", aStream);
|
|
SRFastLog(@"NSStreamEventOpenCompleted %@", aStream);
|
|
@@ -1533,9 +1580,9 @@ static const size_t SRFrameHeaderOverhead = 32;
|
|
dispatch_async(_workQueue, ^{
|
|
dispatch_async(_workQueue, ^{
|
|
if (self.readyState != SR_CLOSED) {
|
|
if (self.readyState != SR_CLOSED) {
|
|
self.readyState = SR_CLOSED;
|
|
self.readyState = SR_CLOSED;
|
|
- _selfRetain = nil;
|
|
|
|
|
|
+ [self _scheduleCleanup];
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if (!_sentClose && !_failed) {
|
|
if (!_sentClose && !_failed) {
|
|
_sentClose = YES;
|
|
_sentClose = YES;
|
|
// If we get closed in this state it's probably not clean because we should be sending this when we send messages
|
|
// If we get closed in this state it's probably not clean because we should be sending this when we send messages
|
|
@@ -1583,7 +1630,6 @@ static const size_t SRFrameHeaderOverhead = 32;
|
|
SRFastLog(@"(default) %@", aStream);
|
|
SRFastLog(@"(default) %@", aStream);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- });
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
@end
|