Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed memory leak and added TSL workaround for iOS 5.0 #98

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions SocketRocket.podspec
@@ -1,11 +1,11 @@
Pod::Spec.new do |s|
s.name = "SocketRocket"
s.version = '0.3.1-beta2'
s.version = '0.3.2-Uken'
s.summary = 'A conforming WebSocket (RFC 6455) client library.'
s.homepage = 'https://github.com/square/SocketRocket'
s.homepage = 'https://github.com/uken/SocketRocket'
s.authors = 'Square'
s.license = 'Apache License, Version 2.0'
s.source = { :git => 'https://github.com/square/SocketRocket.git', :commit => '82c9f8938f8b9b7aa578866cb7ce56bc11e52ced' }
s.source = { :git => 'https://github.com/uken/SocketRocket.git', :tag => s.version.to_s }
s.source_files = 'SocketRocket/*.{h,m,c}'
s.requires_arc = true
s.ios.frameworks = %w{CFNetwork Security}
Expand Down
58 changes: 33 additions & 25 deletions SocketRocket/SRWebSocket.m
Expand Up @@ -381,8 +381,10 @@ - (void)dealloc
[_inputStream close];
[_outputStream close];

sr_dispatch_release(_workQueue);
dispatch_queue_t blockQueue = _workQueue;
_workQueue = NULL;
dispatch_sync( blockQueue, ^{} ); // block until the work queue is empty
sr_dispatch_release(blockQueue);

if (_receivedHTTPHeaders) {
CFRelease(_receivedHTTPHeaders);
Expand Down Expand Up @@ -502,14 +504,14 @@ - (void)_readHTTPHeader;
_receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
}

[self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) {
CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
[self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *webSocket, NSData *data) {
CFHTTPMessageAppendBytes(webSocket->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);

if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
[self _HTTPHeadersDidFinish];
if (CFHTTPMessageIsHeaderComplete(webSocket->_receivedHTTPHeaders)) {
SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(webSocket->_receivedHTTPHeaders)));
[webSocket _HTTPHeadersDidFinish];
} else {
[self _readHTTPHeader];
[webSocket _readHTTPHeader];
}
}];
}
Expand Down Expand Up @@ -576,6 +578,12 @@ - (void)_initializeStreams;

[_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];

if ( [[[UIDevice currentDevice] systemVersion] floatValue] < 5.1 ) {
// TLS workaround for iOS 5.0 devices
// https://developer.apple.com/library/ios/#technotes/tn2287/_index.html#//apple_ref/doc/uid/DTS40011309
[SSLOptions setValue:@"kCFStreamSocketSecurityLevelTLSv1_0SSLv3" forKey:(__bridge id)kCFStreamSSLLevel];
}

// If we're using pinned certs, don't validate the certificate chain
if ([_urlRequest SR_SSLPinnedCertificates].count) {
[SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain];
Expand Down Expand Up @@ -920,15 +928,15 @@ - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData;
}
}
} else {
[self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
[self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *webSocket, NSData *newData) {
if (isControlFrame) {
[self _handleFrameWithData:newData opCode:frame_header.opcode];
[webSocket _handleFrameWithData:newData opCode:frame_header.opcode];
} else {
if (frame_header.fin) {
[self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode];
[webSocket _handleFrameWithData:webSocket->_currentFrameData opCode:frame_header.opcode];
} else {
// TODO add assert that opcode is not a control;
[self _readFrameContinue];
[webSocket _readFrameContinue];
}

}
Expand Down Expand Up @@ -969,32 +977,32 @@ - (void)_readFrameContinue;
{
assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0));

[self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) {
[self _addConsumerWithDataLength:2 callback:^(SRWebSocket *webSocket, NSData *data) {
__block frame_header header = {0};

const uint8_t *headerBuffer = data.bytes;
assert(data.length >= 2);

if (headerBuffer[0] & SRRsvMask) {
[self _closeWithProtocolError:@"Server used RSV bits"];
[webSocket _closeWithProtocolError:@"Server used RSV bits"];
return;
}

uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]);

BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);

if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) {
[self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"];
if (!isControlFrame && receivedOpcode != 0 && webSocket->_currentFrameCount > 0) {
[webSocket _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"];
return;
}

if (receivedOpcode == 0 && self->_currentFrameCount == 0) {
[self _closeWithProtocolError:@"cannot continue a message"];
if (receivedOpcode == 0 && webSocket->_currentFrameCount == 0) {
[webSocket _closeWithProtocolError:@"cannot continue a message"];
return;
}

header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode;
header.opcode = receivedOpcode == 0 ? webSocket->_currentFrameOpcode : receivedOpcode;

header.fin = !!(SRFinMask & headerBuffer[0]);

Expand All @@ -1005,10 +1013,10 @@ - (void)_readFrameContinue;
headerBuffer = NULL;

if (header.masked) {
[self _closeWithProtocolError:@"Client must receive unmasked data"];
[webSocket _closeWithProtocolError:@"Client must receive unmasked data"];
}

size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
size_t extra_bytes_needed = header.masked ? sizeof(webSocket->_currentReadMaskKey) : 0;

if (header.payload_length == 126) {
extra_bytes_needed += sizeof(uint16_t);
Expand All @@ -1017,9 +1025,9 @@ - (void)_readFrameContinue;
}

if (extra_bytes_needed == 0) {
[self _handleFrameHeader:header curData:self->_currentFrameData];
[webSocket _handleFrameHeader:header curData:webSocket->_currentFrameData];
} else {
[self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) {
[webSocket _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *webSocket, NSData *data) {
size_t mapped_size = data.length;
const void *mapped_buffer = data.bytes;
size_t offset = 0;
Expand All @@ -1039,11 +1047,11 @@ - (void)_readFrameContinue;


if (header.masked) {
assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
assert(mapped_size >= sizeof(webSocket->_currentReadMaskOffset) + offset);
memcpy(webSocket->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(webSocket->_currentReadMaskKey));
}

[self _handleFrameHeader:header curData:self->_currentFrameData];
[webSocket _handleFrameHeader:header curData:webSocket->_currentFrameData];
} readToCurrentFrame:NO unmaskBytes:NO];
}
} readToCurrentFrame:NO unmaskBytes:NO];
Expand Down