bump websocket

This commit is contained in:
Erik 2015-09-26 16:49:34 -04:00
parent f933b7addc
commit a005646054
2 changed files with 169 additions and 192 deletions

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,23 +128,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
if isCreated { if isCreated {
return return
} }
dispatch_async(queue,{ [weak self] in
dispatch_async(queue, { [weak self] in self?.didDisconnect = false
guard let weakSelf = self else { })
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
})
} }
///disconnect from the websocket server ///disconnect from the websocket server
@ -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,17 +370,18 @@ 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
if restSize > 0 { if restSize > 0 {
processRawMessage((buffer+totalSize), bufferLen: restSize) processRawMessage((buffer+totalSize),bufferLen: restSize)
} }
return true return true
} }
@ -403,11 +396,12 @@ 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,10 +511,13 @@ 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))
let extra = bufferLen-step let extra = bufferLen-step
@ -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() return
writeQueue!.maxConcurrentOperationCount = 1
} }
writeQueue!.addOperationWithBlock { writeQueue.addOperationWithBlock { [weak self] in
//stream isn't ready, let's wait //stream isn't ready, let's wait
var tries = 0; guard let s = self else { return }
while self.outputStream == nil || !self.connected {
if(tries < 5) {
sleep(1);
} else {
break;
}
tries++;
}
if !self.connected {
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,14 +883,14 @@ 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)
if let cert = possibleCert { if let cert = possibleCert {
return extractPublicKeyFromCert(cert,policy: SecPolicyCreateBasicX509()) return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
} }
return nil return nil
} }
@ -927,16 +898,16 @@ 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)
} }
return nil return nil
@ -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>()