Merge branch 'master' into travis

* master:
  bump websocket
  bump version
This commit is contained in:
Erik 2015-09-26 16:55:38 -04:00
commit 3f8941d35b
4 changed files with 173 additions and 196 deletions

View File

@ -64,7 +64,7 @@ Carthage
----------------- -----------------
Add this line to your `Cartfile`: Add this line to your `Cartfile`:
``` ```
github "socketio/socket.io-client-swift" ~> 3.1.1 # Or latest version github "socketio/socket.io-client-swift" ~> 3.1.2 # Or latest version
``` ```
Run `carthage update --platform ios,macosx`. Run `carthage update --platform ios,macosx`.
@ -83,7 +83,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
use_frameworks! use_frameworks!
pod 'Socket.IO-Client-Swift', '~> 3.1.1' # Or latest version pod 'Socket.IO-Client-Swift', '~> 3.1.2' # Or latest version
``` ```
Install pods: Install pods:

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.version = "3.1.1" s.version = "3.1.2"
s.summary = "Socket.IO-client for iOS and OS X" s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for iOS and OS X. Socket.IO-client for iOS and OS X.
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.author = { "Erik" => "nuclear.ace@gmail.com" } s.author = { "Erik" => "nuclear.ace@gmail.com" }
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10' s.osx.deployment_target = '10.10'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v3.1.1' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v3.1.2' }
s.source_files = "SocketIOClientSwift/**/*.swift" s.source_files = "SocketIOClientSwift/**/*.swift"
s.requires_arc = true s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -191,8 +191,14 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
private func createWebsocketAndConnect(connect: Bool) { private func createWebsocketAndConnect(connect: Bool) {
let wsUrl = urlWebSocket + (sid == "" ? "" : "&sid=\(sid)") let wsUrl = urlWebSocket + (sid == "" ? "" : "&sid=\(sid)")
ws = WebSocket(url: NSURL(string: wsUrl)!, ws = WebSocket(url: NSURL(string: wsUrl)!)
cookies: cookies)
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
for (key, value) in headers {
ws?.headers[key] = value
}
}
if extraHeaders != nil { if extraHeaders != nil {
for (headerName, value) in extraHeaders! { for (headerName, value) in extraHeaders! {

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import CoreFoundation import CoreFoundation
import Security
public protocol WebSocketDelegate: class { public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket) func websocketDidConnect(socket: WebSocket)
@ -93,19 +94,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
public var headers = Dictionary<String,String>() public var headers = Dictionary<String,String>()
public var voipEnabled = false public var voipEnabled = false
public var selfSignedSSL = false public var selfSignedSSL = false
private var security: Security? private var security: SSLSecurity?
public var enabledSSLCipherSuites: [SSLCipherSuite]?
public var isConnected :Bool { public var isConnected :Bool {
return connected return connected
} }
private var cookies:[NSHTTPCookie]?
private var url: NSURL private var url: NSURL
private var inputStream: NSInputStream? private var inputStream: NSInputStream?
private var outputStream: NSOutputStream? private var outputStream: NSOutputStream?
private var isRunLoop = false private var isRunLoop = false
private var connected = false private var connected = false
private var isCreated = false private var isCreated = false
private var writeQueue: NSOperationQueue? private var writeQueue = NSOperationQueue()
private var readStack = Array<WSResponse>() private var readStack = Array<WSResponse>()
private var inputQueue = Array<NSData>() private var inputQueue = Array<NSData>()
private var fragBuffer: NSData? private var fragBuffer: NSData?
@ -115,13 +115,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
//init the websocket with a url //init the websocket with a url
public init(url: NSURL) { public init(url: NSURL) {
self.url = url self.url = url
writeQueue.maxConcurrentOperationCount = 1
} }
public convenience init(url: NSURL, cookies:[NSHTTPCookie]?) {
self.init(url: url)
self.cookies = cookies
}
//used for setting protocols. //used for setting protocols.
public convenience init(url: NSURL, protocols: Array<String>) { public convenience init(url: NSURL, protocols: Array<String>) {
self.init(url: url) self.init(url: url)
@ -133,22 +128,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
if isCreated { if isCreated {
return return
} }
dispatch_async(queue,{ [weak self] in dispatch_async(queue,{ [weak self] in
guard let weakSelf = self else { self?.didDisconnect = false
return
}
weakSelf.didDisconnect = false
}) })
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { [weak self] in dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { [weak self] in
guard let weakSelf = self else { self?.isCreated = true
return self?.createHTTPRequest()
} self?.isCreated = false
weakSelf.isCreated = true
weakSelf.createHTTPRequest()
weakSelf.isCreated = false
}) })
} }
@ -188,30 +174,22 @@ public class WebSocket : NSObject, NSStreamDelegate {
port = 80 port = 80
} }
} }
addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
if self.cookies != nil { addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(self.cookies!)
for (key, value) in headers {
self.addHeader(urlRequest, key: key as String, val: value as String)
}
}
self.addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
self.addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
if let protocols = optionalProtocols { if let protocols = optionalProtocols {
self.addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(",")) addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(","))
} }
self.addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue) addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
self.addHeader(urlRequest, key: headerWSKeyName, val: self.generateWebSocketKey()) addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
self.addHeader(urlRequest, key: headerOriginName, val: url.absoluteString) addHeader(urlRequest, key: headerOriginName, val: url.absoluteString)
self.addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
for (key,value) in headers { for (key,value) in headers {
self.addHeader(urlRequest, key: key, val: value) addHeader(urlRequest, key: key, val: value)
}
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
let serializedRequest = cfHTTPMessage.takeRetainedValue()
initStreamsWithData(serializedRequest, Int(port!))
} }
let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest)!.takeRetainedValue()
self.initStreamsWithData(serializedRequest, Int(port!))
} }
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString //Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: CFHTTPMessage,key: String, val: String) { private func addHeader(urlRequest: CFHTTPMessage,key: String, val: String) {
@ -244,31 +222,48 @@ public class WebSocket : NSObject, NSStreamDelegate {
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream) CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue() inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue() outputStream = writeStream!.takeRetainedValue()
guard let inStream = inputStream, let outStream = outputStream else { return }
inputStream!.delegate = self inStream.delegate = self
outputStream!.delegate = self outStream.delegate = self
if url.scheme == "wss" || url.scheme == "https" { if url.scheme == "wss" || url.scheme == "https" {
inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
} else { } else {
certValidated = true //not a https session, so no need to check SSL pinning certValidated = true //not a https session, so no need to check SSL pinning
} }
if self.voipEnabled { if voipEnabled {
inputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
outputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
} }
if self.selfSignedSSL { if selfSignedSSL {
let settings: Dictionary<NSObject, NSObject> = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull] let settings: Dictionary<NSObject, NSObject> = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull]
inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
}
if let cipherSuites = self.enabledSSLCipherSuites {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?,
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
if (resIn != errSecSuccess) {
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
disconnectStream(error)
return
}
if (resOut != errSecSuccess) {
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
disconnectStream(error)
return
}
}
} }
isRunLoop = true isRunLoop = true
inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) inStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) outStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream!.open() inStream.open()
outputStream!.open() outStream.open()
let bytes = UnsafePointer<UInt8>(data.bytes) let bytes = UnsafePointer<UInt8>(data.bytes)
outputStream!.write(bytes, maxLength: data.length) outStream.write(bytes, maxLength: data.length)
while(isRunLoop) { while(isRunLoop) {
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as NSDate) NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as NSDate)
} }
@ -283,8 +278,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) { if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) {
certValidated = true certValidated = true
} else { } else {
let error = self.errorWithDetail("Invalid SSL certificate", code: 1) let error = errorWithDetail("Invalid SSL certificate", code: 1)
doDisconnect(error)
disconnectStream(error) disconnectStream(error)
return return
} }
@ -302,9 +296,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
//disconnect the stream object //disconnect the stream object
private func disconnectStream(error: NSError?) { private func disconnectStream(error: NSError?) {
if writeQueue != nil { writeQueue.waitUntilAllOperationsAreFinished()
writeQueue!.waitUntilAllOperationsAreFinished()
}
if let stream = inputStream { if let stream = inputStream {
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
stream.close() stream.close()
@ -316,7 +308,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
outputStream = nil outputStream = nil
isRunLoop = false isRunLoop = false
certValidated = false certValidated = false
self.doDisconnect(error) doDisconnect(error)
connected = false connected = false
} }
@ -329,7 +321,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
if !connected { if !connected {
let status = processHTTP(buffer, bufferLen: length) let status = processHTTP(buffer, bufferLen: length)
if !status { if !status {
self.doDisconnect(self.errorWithDetail("Invalid HTTP upgrade", code: 1)) doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: 1))
} }
} else { } else {
var process = false var process = false
@ -348,7 +340,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
if inputQueue.count > 0 { if inputQueue.count > 0 {
let data = inputQueue[0] let data = inputQueue[0]
var work = data var work = data
if (fragBuffer != nil) { if fragBuffer != nil {
let combine = NSMutableData(data: fragBuffer!) let combine = NSMutableData(data: fragBuffer!)
combine.appendData(data) combine.appendData(data)
work = combine work = combine
@ -378,12 +370,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
if totalSize > 0 { if totalSize > 0 {
if validateResponse(buffer, bufferLen: totalSize) { if validateResponse(buffer, bufferLen: totalSize) {
dispatch_async(queue, { dispatch_async(queue,{ [weak self] in
self.connected = true guard let s = self else { return }
if let connectBlock = self.onConnect { s.connected = true
if let connectBlock = s.onConnect {
connectBlock() connectBlock()
} }
self.delegate?.websocketDidConnect(self) s.delegate?.websocketDidConnect(s)
}) })
totalSize += 1 //skip the last \n totalSize += 1 //skip the last \n
let restSize = bufferLen - totalSize let restSize = bufferLen - totalSize
@ -403,12 +396,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
if CFHTTPMessageGetResponseStatusCode(response) != 101 { if CFHTTPMessageGetResponseStatusCode(response) != 101 {
return false return false
} }
let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers:NSDictionary? = cfHeaders?.takeRetainedValue() let headers = cfHeaders.takeRetainedValue() as NSDictionary
let acceptKey = headers?[headerWSAcceptName] as! NSString let acceptKey = headers[headerWSAcceptName] as! NSString
if acceptKey.length > 0 { if acceptKey.length > 0 {
return true return true
} }
}
return false return false
} }
@ -443,8 +437,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
var offset = 2 var offset = 2
if((isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != OpCode.Pong.rawValue) { if((isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != OpCode.Pong.rawValue) {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("masked and rsv data is not currently supported", code: errCode) doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
@ -452,15 +445,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
if !isControlFrame && (receivedOpcode != OpCode.BinaryFrame.rawValue && receivedOpcode != OpCode.ContinueFrame.rawValue && if !isControlFrame && (receivedOpcode != OpCode.BinaryFrame.rawValue && receivedOpcode != OpCode.ContinueFrame.rawValue &&
receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) { receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode) doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
if isControlFrame && isFin == 0 { if isControlFrame && isFin == 0 {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("control frames can't be fragmented", code: errCode) doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
@ -486,8 +477,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
} }
} }
let error = self.errorWithDetail("connection closed by server", code: code) doDisconnect(errorWithDetail("connection closed by server", code: code))
self.doDisconnect(error)
writeError(code) writeError(code)
return return
} }
@ -521,9 +511,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len)) data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
} }
if receivedOpcode == OpCode.Pong.rawValue { if receivedOpcode == OpCode.Pong.rawValue {
dispatch_async(queue, { dispatch_async(queue,{ [weak self] in
self.onPong?() guard let s = self else { return }
self.pongDelegate?.websocketDidReceivePong(self) if let pongBlock = s.onPong {
pongBlock()
}
s.pongDelegate?.websocketDidReceivePong(s)
}) })
let step = Int(offset+numericCast(len)) let step = Int(offset+numericCast(len))
@ -539,8 +532,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil { if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("continue frame before a binary or text frame", code: errCode) doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
@ -548,9 +540,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
if(response == nil) { if(response == nil) {
if receivedOpcode == OpCode.ContinueFrame.rawValue { if receivedOpcode == OpCode.ContinueFrame.rawValue {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("first frame can't be a continue frame", doDisconnect(errorWithDetail("first frame can't be a continue frame",
code: errCode) code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
@ -564,9 +555,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
response!.bytesLeft = Int(dataLength) response!.bytesLeft = Int(dataLength)
} else { } else {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("second and beyond of fragment message must be a continue frame", doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
code: errCode) code: errCode))
self.doDisconnect(error)
writeError(errCode) writeError(errCode)
return return
} }
@ -608,24 +598,28 @@ public class WebSocket : NSObject, NSStreamDelegate {
dequeueWrite(data, code: OpCode.Pong) dequeueWrite(data, code: OpCode.Pong)
} else if response.code == .TextFrame { } else if response.code == .TextFrame {
let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding) let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
if str == nil {
if let str = str as String? {
dispatch_async(queue, {
self.onText?(str)
self.delegate?.websocketDidReceiveMessage(self, text: str)
})
} else {
writeError(CloseCode.Encoding.rawValue) writeError(CloseCode.Encoding.rawValue)
return false return false
} }
dispatch_async(queue,{ [weak self] in
guard let s = self else { return }
if let textBlock = s.onText {
textBlock(str! as String)
}
s.delegate?.websocketDidReceiveMessage(s, text: str! as String)
})
} else if response.code == .BinaryFrame { } else if response.code == .BinaryFrame {
let data = response.buffer! //local copy so it is perverse for writing let data = response.buffer! //local copy so it is perverse for writing
dispatch_async(queue) { dispatch_async(queue,{ [weak self] in
self.onData?(data) guard let s = self else { return }
self.delegate?.websocketDidReceiveData(self, data: data) if let dataBlock = s.onData {
dataBlock(data)
} }
s.delegate?.websocketDidReceiveData(s, data: data)
})
} }
readStack.removeLast() readStack.removeLast()
return true return true
} }
@ -648,31 +642,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
///used to write things to the stream ///used to write things to the stream
private func dequeueWrite(data: NSData, code: OpCode) { private func dequeueWrite(data: NSData, code: OpCode) {
if writeQueue == nil { if !isConnected {
writeQueue = NSOperationQueue()
writeQueue!.maxConcurrentOperationCount = 1
}
writeQueue!.addOperationWithBlock {
//stream isn't ready, let's wait
var tries = 0;
while self.outputStream == nil || !self.connected {
if(tries < 5) {
sleep(1);
} else {
break;
}
tries++;
}
if !self.connected {
return return
} }
writeQueue.addOperationWithBlock { [weak self] in
//stream isn't ready, let's wait
guard let s = self else { return }
var offset = 2 var offset = 2
UINT16_MAX
let bytes = UnsafeMutablePointer<UInt8>(data.bytes) let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
let dataLength = data.length let dataLength = data.length
let frame = NSMutableData(capacity: dataLength + self.MaxFrameSize) let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes) let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
buffer[0] = self.FinMask | code.rawValue buffer[0] = s.FinMask | code.rawValue
if dataLength < 126 { if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength) buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) { } else if dataLength <= Int(UInt16.max) {
@ -686,7 +667,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
sizeBuffer[0] = UInt64(dataLength).bigEndian sizeBuffer[0] = UInt64(dataLength).bigEndian
offset += sizeof(UInt64) offset += sizeof(UInt64)
} }
buffer[1] |= self.MaskMask buffer[1] |= s.MaskMask
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset) let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey) SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32) offset += sizeof(UInt32)
@ -697,23 +678,24 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
var total = 0 var total = 0
while true { while true {
if self.outputStream == nil { if !s.isConnected {
break break
} }
guard let outStream = s.outputStream else { break }
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total) let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
let len = self.outputStream?.write(writeBuffer, maxLength: offset-total) let len = outStream.write(writeBuffer, maxLength: offset-total)
if len == nil || len! < 0 { if len < 0 {
var error: NSError? var error: NSError?
if let streamError = self.outputStream?.streamError { if let streamError = outStream.streamError {
error = streamError error = streamError
} else { } else {
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
error = self.errorWithDetail("output stream error during write", code: errCode) error = s.errorWithDetail("output stream error during write", code: errCode)
} }
self.doDisconnect(error) s.doDisconnect(error)
break break
} else { } else {
total += len! total += len
} }
if total >= offset { if total >= offset {
break break
@ -725,31 +707,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
///used to preform the disconnect delegate ///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) { private func doDisconnect(error: NSError?) {
if !self.didDisconnect { if !didDisconnect {
dispatch_async(queue) { dispatch_async(queue,{ [weak self] in
self.didDisconnect = true guard let s = self else { return }
s.didDisconnect = true
self.onDisconnect?(error) if let disconnect = s.onDisconnect {
self.delegate?.websocketDidDisconnect(self, error: error) disconnect(error)
} }
s.delegate?.websocketDidDisconnect(s, error: error)
})
} }
} }
} }
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Security.swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2015 Vluxe. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import Security
private class SSLCert { private class SSLCert {
var certData: NSData? var certData: NSData?
var key: SecKeyRef? var key: SecKeyRef?
@ -757,9 +728,9 @@ private class SSLCert {
/** /**
Designated init for certificates Designated init for certificates
:param: data is the binary data of the certificate - parameter data: is the binary data of the certificate
:returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
init(data: NSData) { init(data: NSData) {
self.certData = data self.certData = data
@ -768,17 +739,17 @@ private class SSLCert {
/** /**
Designated init for public keys Designated init for public keys
:param: key is the public key to be used - parameter key: is the public key to be used
:returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
init(key: SecKeyRef) { init(key: SecKeyRef) {
self.key = key self.key = key
} }
} }
private class Security { private class SSLSecurity {
private var validatedDN = true //should the domain name be validated? var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done? var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates var certificates: [NSData]? //the certificates
@ -788,11 +759,11 @@ private class Security {
/** /**
Use certs from main app bundle Use certs from main app bundle
:param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation - parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
:returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
private convenience init(usePublicKeys: Bool = false) { convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".") let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
var collect = Array<SSLCert>() var collect = Array<SSLCert>()
for path in paths { for path in paths {
@ -806,12 +777,12 @@ private class Security {
/** /**
Designated init Designated init
:param: keys is the certificates or public keys to use - parameter keys: is the certificates or public keys to use
:param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation - parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
:returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
private init(certs: [SSLCert], usePublicKeys: Bool) { init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys self.usePublicKeys = usePublicKeys
if self.usePublicKeys { if self.usePublicKeys {
@ -843,12 +814,12 @@ private class Security {
/** /**
Valid the trust and domain name. Valid the trust and domain name.
:param: trust is the serverTrust to validate - parameter trust: is the serverTrust to validate
:param: domain is the CN domain to validate - parameter domain: is the CN domain to validate
:returns: if the key was successfully validated - returns: if the key was successfully validated
*/ */
private func isValid(trust: SecTrustRef, domain: String?) -> Bool { func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0 var tries = 0
while(!self.isReady) { while(!self.isReady) {
@ -912,9 +883,9 @@ private class Security {
/** /**
Get the public key from a certificate data Get the public key from a certificate data
:param: data is the certificate to pull the public key from - parameter data: is the certificate to pull the public key from
:returns: a public key - returns: a public key
*/ */
func extractPublicKey(data: NSData) -> SecKeyRef? { func extractPublicKey(data: NSData) -> SecKeyRef? {
let possibleCert = SecCertificateCreateWithData(nil,data) let possibleCert = SecCertificateCreateWithData(nil,data)
@ -927,14 +898,14 @@ private class Security {
/** /**
Get the public key from a certificate Get the public key from a certificate
:param: data is the certificate to pull the public key from - parameter data: is the certificate to pull the public key from
:returns: a public key - returns: a public key
*/ */
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? { func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
let possibleTrust = UnsafeMutablePointer<SecTrust?>.alloc(1) var possibleTrust: SecTrust?
SecTrustCreateWithCertificates( cert, policy, possibleTrust) SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
if let trust = possibleTrust.memory { if let trust = possibleTrust {
var result: SecTrustResultType = 0 var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result) SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust) return SecTrustCopyPublicKey(trust)
@ -945,9 +916,9 @@ private class Security {
/** /**
Get the certificate chain for the trust Get the certificate chain for the trust
:param: trust is the trust to lookup the certificate chain for - parameter trust: is the trust to lookup the certificate chain for
:returns: the certificate chain for the trust - returns: the certificate chain for the trust
*/ */
func certificateChainForTrust(trust: SecTrustRef) -> Array<NSData> { func certificateChainForTrust(trust: SecTrustRef) -> Array<NSData> {
var collect = Array<NSData>() var collect = Array<NSData>()
@ -961,9 +932,9 @@ private class Security {
/** /**
Get the public key chain for the trust Get the public key chain for the trust
:param: trust is the trust to lookup the certificate chain and extract the public keys - parameter trust: is the trust to lookup the certificate chain and extract the public keys
:returns: the public keys from the certifcate chain for the trust - returns: the public keys from the certifcate chain for the trust
*/ */
func publicKeyChainForTrust(trust: SecTrustRef) -> Array<SecKeyRef> { func publicKeyChainForTrust(trust: SecTrustRef) -> Array<SecKeyRef> {
var collect = Array<SecKeyRef>() var collect = Array<SecKeyRef>()