Merge branch 'development' of https://github.com/socketio/socket.io-client-swift into development

* 'development' of https://github.com/socketio/socket.io-client-swift:
  SocketAckEmitter.isExpected => .expected
  change isRequired() to isExpected()
  remove semicolon
  Update README.md
  add SocketAckEmitter.isRequired()
  Update README.md
  bump version
  bump websocket version
  fix Use Legacy Swift issue
  bump websocket version
  Update README.md
This commit is contained in:
Erik 2016-11-11 15:52:48 -05:00
commit 56278795ea
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
5 changed files with 78 additions and 74 deletions

View File

@ -39,7 +39,7 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
[socket on:@"currentAmount" callback:^(NSArray* data, SocketAckEmitter* ack) { [socket on:@"currentAmount" callback:^(NSArray* data, SocketAckEmitter* ack) {
double cur = [[data objectAtIndex:0] floatValue]; double cur = [[data objectAtIndex:0] floatValue];
[[socket emitWithAck:@"canUpdate" withItems:@[@(cur)]] timingOutAfter:0 callback:^(NSArray* data) { [[socket emitWithAck:@"canUpdate" with:@[@(cur)]] timingOutAfter:0 callback:^(NSArray* data) {
[socket emit:@"update" withItems:@[@{@"amount": @(cur + 2.50)}]]; [socket emit:@"update" withItems:@[@{@"amount": @(cur + 2.50)}]];
}]; }];
@ -95,7 +95,7 @@ Carthage
----------------- -----------------
Add this line to your `Cartfile`: Add this line to your `Cartfile`:
``` ```
github "socketio/socket.io-client-swift" ~> 8.0.2 # Or latest version github "socketio/socket.io-client-swift" ~> 8.1.0 # Or latest version
``` ```
Run `carthage update --platform ios,macosx`. Run `carthage update --platform ios,macosx`.
@ -108,7 +108,7 @@ Create `Podfile` and add `pod 'Socket.IO-Client-Swift'`:
use_frameworks! use_frameworks!
target 'YourApp' do target 'YourApp' do
pod 'Socket.IO-Client-Swift', '~> 8.0.2' # Or latest version pod 'Socket.IO-Client-Swift', '~> 8.1.0' # Or latest version
end end
``` ```
@ -137,7 +137,7 @@ CocoaSeeds
Add this line to your `Seedfile`: Add this line to your `Seedfile`:
``` ```
github "socketio/socket.io-client-swift", "v8.0.2", :files => "Source/*.swift" # Or latest version github "socketio/socket.io-client-swift", "v8.1.0", :files => "Source/*.swift" # Or latest version
``` ```
Run `seed install`. Run `seed install`.

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIO" s.module_name = "SocketIO"
s.version = "8.0.2" s.version = "8.1.0"
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.
@ -14,8 +14,9 @@ Pod::Spec.new do |s|
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.tvos.deployment_target = '9.0' s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v8.0.2' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v8.1.0' }
s.source_files = "Source/**/*.swift" s.source_files = "Source/**/*.swift"
s.requires_arc = true s.requires_arc = true
s.pod_target_xcconfig = {'SWIFT_VERSION' => '3.0'}
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files
end end

View File

@ -4,7 +4,7 @@
// Starscream // Starscream
// //
// Created by Dalton Cherry on 5/16/15. // Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2014-2015 Dalton Cherry. // Copyright (c) 2014-2016 Dalton Cherry.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -19,11 +19,14 @@
// limitations under the License. // limitations under the License.
// //
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation import Foundation
import Security import Security
public class SSLCert : NSObject { public protocol SSLTrustValidator {
func isValid(_ trust: SecTrust, domain: String?) -> Bool
}
open class SSLCert {
var certData: Data? var certData: Data?
var key: SecKey? var key: SecKey?
@ -50,7 +53,7 @@ public class SSLCert : NSObject {
} }
} }
public class SSLSecurity : NSObject { open class SSLSecurity : SSLTrustValidator {
public var validatedDN = true //should the domain name be validated? public 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?
@ -82,7 +85,7 @@ public class SSLSecurity : NSObject {
/** /**
Designated init Designated init
- parameter keys: is the certificates or public keys to use - parameter certs: is the certificates or public keys to use
- parameter 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
@ -90,8 +93,6 @@ public class SSLSecurity : NSObject {
public init(certs: [SSLCert], usePublicKeys: Bool) { public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys self.usePublicKeys = usePublicKeys
super.init()
if self.usePublicKeys { if self.usePublicKeys {
DispatchQueue.global(qos: .default).async { DispatchQueue.global(qos: .default).async {
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in

View File

@ -27,6 +27,10 @@ import Foundation
public final class SocketAckEmitter : NSObject { public final class SocketAckEmitter : NSObject {
let socket: SocketIOClient let socket: SocketIOClient
let ackNum: Int let ackNum: Int
public var expected: Bool {
return ackNum != -1
}
init(socket: SocketIOClient, ackNum: Int) { init(socket: SocketIOClient, ackNum: Int) {
self.socket = socket self.socket = socket
@ -44,6 +48,7 @@ public final class SocketAckEmitter : NSObject {
socket.emitAck(ackNum, with: items) socket.emitAck(ackNum, with: items)
} }
} }
public final class OnAckCallback : NSObject { public final class OnAckCallback : NSObject {

View File

@ -3,7 +3,7 @@
// Websocket.swift // Websocket.swift
// //
// Created by Dalton Cherry on 7/16/14. // Created by Dalton Cherry on 7/16/14.
// Copyright (c) 2014-2015 Dalton Cherry. // Copyright (c) 2014-2016 Dalton Cherry.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@
// limitations under the License. // limitations under the License.
// //
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation import Foundation
import CoreFoundation import CoreFoundation
import Security import Security
@ -38,7 +37,7 @@ public protocol WebSocketPongDelegate: class {
func websocketDidReceivePong(socket: WebSocket, data: Data?) func websocketDidReceivePong(socket: WebSocket, data: Data?)
} }
public class WebSocket : NSObject, StreamDelegate { open class WebSocket : NSObject, StreamDelegate {
enum OpCode : UInt8 { enum OpCode : UInt8 {
case continueFrame = 0x0 case continueFrame = 0x0
@ -77,7 +76,6 @@ public class WebSocket : NSObject, StreamDelegate {
var optionalProtocols: [String]? var optionalProtocols: [String]?
// MARK: - Constants // MARK: - Constants
let headerWSUpgradeName = "Upgrade" let headerWSUpgradeName = "Upgrade"
let headerWSUpgradeValue = "websocket" let headerWSUpgradeValue = "websocket"
let headerWSHostName = "Host" let headerWSHostName = "Host"
@ -108,7 +106,6 @@ public class WebSocket : NSObject, StreamDelegate {
} }
// MARK: - Delegates // MARK: - Delegates
/// Responds to callback about new messages coming in over the WebSocket /// Responds to callback about new messages coming in over the WebSocket
/// and also connection/disconnect messages. /// and also connection/disconnect messages.
public weak var delegate: WebSocketDelegate? public weak var delegate: WebSocketDelegate?
@ -118,7 +115,6 @@ public class WebSocket : NSObject, StreamDelegate {
// MARK: - Block based API. // MARK: - Block based API.
public var onConnect: ((Void) -> Void)? public var onConnect: ((Void) -> Void)?
public var onDisconnect: ((NSError?) -> Void)? public var onDisconnect: ((NSError?) -> Void)?
public var onText: ((String) -> Void)? public var onText: ((String) -> Void)?
@ -128,7 +124,7 @@ public class WebSocket : NSObject, StreamDelegate {
public var headers = [String: String]() public var headers = [String: String]()
public var voipEnabled = false public var voipEnabled = false
public var disableSSLCertValidation = false public var disableSSLCertValidation = false
public var security: SSLSecurity? public var security: SSLTrustValidator?
public var enabledSSLCipherSuites: [SSLCipherSuite]? public var enabledSSLCipherSuites: [SSLCipherSuite]?
public var origin: String? public var origin: String?
public var timeout = 5 public var timeout = 5
@ -139,7 +135,6 @@ public class WebSocket : NSObject, StreamDelegate {
public var currentURL: URL { return url } public var currentURL: URL { return url }
// MARK: - Private // MARK: - Private
private var url: URL private var url: URL
private var inputStream: InputStream? private var inputStream: InputStream?
private var outputStream: OutputStream? private var outputStream: OutputStream?
@ -198,7 +193,8 @@ public class WebSocket : NSObject, StreamDelegate {
public func disconnect(forceTimeout: TimeInterval? = nil, closeCode: UInt16 = CloseCode.normal.rawValue) { public func disconnect(forceTimeout: TimeInterval? = nil, closeCode: UInt16 = CloseCode.normal.rawValue) {
switch forceTimeout { switch forceTimeout {
case .some(let seconds) where seconds > 0: case .some(let seconds) where seconds > 0:
callbackQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { [weak self] in let milliseconds = Int(seconds * 1_000)
callbackQueue.asyncAfter(deadline: .now() + .milliseconds(milliseconds)) { [weak self] in
self?.disconnectStream(nil) self?.disconnectStream(nil)
} }
fallthrough fallthrough
@ -213,7 +209,7 @@ public class WebSocket : NSObject, StreamDelegate {
/** /**
Write a string to the websocket. This sends it as a text frame. Write a string to the websocket. This sends it as a text frame.
If you supply a non-nil completion block, I will perform it when the write completes. If you supply a non-nil completion block, I will perform it when the write completes.
- parameter str: The string to write. - parameter string: The string to write.
- parameter completion: The (optional) completion handler. - parameter completion: The (optional) completion handler.
*/ */
public func write(string: String, completion: (() -> ())? = nil) { public func write(string: String, completion: (() -> ())? = nil) {
@ -305,7 +301,6 @@ public class WebSocket : NSObject, StreamDelegate {
private func initStreamsWithData(_ data: Data, _ port: Int) { private func initStreamsWithData(_ data: Data, _ port: Int) {
//higher level API we will cut over to at some point //higher level API we will cut over to at some point
//NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream) //NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream)
var readStream: Unmanaged<CFReadStream>? var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>? var writeStream: Unmanaged<CFWriteStream>?
let h = url.host! as NSString let h = url.host! as NSString
@ -318,6 +313,28 @@ public class WebSocket : NSObject, StreamDelegate {
if supportedSSLSchemes.contains(url.scheme!) { if supportedSSLSchemes.contains(url.scheme!) {
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey) inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey) outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
if disableSSLCertValidation {
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
}
if let cipherSuites = self.enabledSSLCipherSuites {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext?,
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
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
}
}
}
} 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
} }
@ -325,28 +342,6 @@ public class WebSocket : NSObject, StreamDelegate {
inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType) inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType) outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
} }
if disableSSLCertValidation {
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
}
if let cipherSuites = self.enabledSSLCipherSuites {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext?,
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
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
}
}
}
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue) CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
@ -358,7 +353,7 @@ public class WebSocket : NSObject, StreamDelegate {
self.mutex.unlock() self.mutex.unlock()
let bytes = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self) let bytes = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
var out = timeout * 1000000 // wait 5 seconds before giving up var out = timeout * 1_000_000 // wait 5 seconds before giving up
writeQueue.addOperation { [weak self] in writeQueue.addOperation { [weak self] in
while !outStream.hasSpaceAvailable { while !outStream.hasSpaceAvailable {
usleep(100) // wait until the socket is ready usleep(100) // wait until the socket is ready
@ -380,9 +375,9 @@ public class WebSocket : NSObject, StreamDelegate {
*/ */
public func stream(_ aStream: Stream, handle eventCode: Stream.Event) { public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
if let sec = security, !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) { if let sec = security, !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) {
let trust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as AnyObject let trust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust
let domain = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String let domain = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
if sec.isValid(trust as! SecTrust, domain: domain) { if sec.isValid(trust, domain: domain) {
certValidated = true certValidated = true
} else { } else {
let error = errorWithDetail("Invalid SSL certificate", code: 1) let error = errorWithDetail("Invalid SSL certificate", code: 1)
@ -439,7 +434,6 @@ public class WebSocket : NSObject, StreamDelegate {
let buf = NSMutableData(capacity: BUFFER_MAX) let buf = NSMutableData(capacity: BUFFER_MAX)
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
guard length > 0 else { return } guard length > 0 else { return }
var process = false var process = false
if inputQueue.count == 0 { if inputQueue.count == 0 {
@ -635,34 +629,22 @@ public class WebSocket : NSObject, StreamDelegate {
writeError(errCode) writeError(errCode)
return emptyBuffer return emptyBuffer
} }
var closeCode = CloseCode.normal.rawValue
if receivedOpcode == .connectionClose { if receivedOpcode == .connectionClose {
var code = CloseCode.normal.rawValue
if payloadLen == 1 { if payloadLen == 1 {
code = CloseCode.protocolError.rawValue closeCode = CloseCode.protocolError.rawValue
} else if payloadLen > 1 { } else if payloadLen > 1 {
code = WebSocket.readUint16(baseAddress, offset: offset) closeCode = WebSocket.readUint16(baseAddress, offset: offset)
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) { if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1011 && closeCode < 3000) {
code = CloseCode.protocolError.rawValue closeCode = CloseCode.protocolError.rawValue
}
offset += 2
}
var closeReason = "connection closed by server"
if payloadLen > 2 {
let len = Int(payloadLen - 2)
if len > 0 {
let bytes = baseAddress + offset
if let customCloseReason = String(data: Data(bytes: bytes, count: len), encoding: .utf8) {
closeReason = customCloseReason
} else {
code = CloseCode.protocolError.rawValue
}
} }
} }
doDisconnect(errorWithDetail(closeReason, code: code)) if payloadLen < 2 {
writeError(code) doDisconnect(errorWithDetail("connection closed by server", code: closeCode))
return emptyBuffer writeError(closeCode)
} return emptyBuffer
if isControlFrame && payloadLen > 125 { }
} else if isControlFrame && payloadLen > 125 {
writeError(CloseCode.protocolError.rawValue) writeError(CloseCode.protocolError.rawValue)
return emptyBuffer return emptyBuffer
} }
@ -687,8 +669,24 @@ public class WebSocket : NSObject, StreamDelegate {
len = 0 len = 0
data = Data() data = Data()
} else { } else {
if receivedOpcode == .connectionClose && len > 0 {
let size = MemoryLayout<UInt16>.size
offset += size
len -= UInt64(size)
}
data = Data(bytes: baseAddress+offset, count: Int(len)) data = Data(bytes: baseAddress+offset, count: Int(len))
} }
if receivedOpcode == .connectionClose {
var closeReason = "connection closed by server"
if let customCloseReason = String(data: data, encoding: .utf8) {
closeReason = customCloseReason
} else {
closeCode = CloseCode.protocolError.rawValue
}
doDisconnect(errorWithDetail(closeReason, code: closeCode))
writeError(closeCode)
return emptyBuffer
}
if receivedOpcode == .pong { if receivedOpcode == .pong {
if canDispatch { if canDispatch {
callbackQueue.async { [weak self] in callbackQueue.async { [weak self] in
@ -902,7 +900,6 @@ public class WebSocket : NSObject, StreamDelegate {
} }
// MARK: - Deinit // MARK: - Deinit
deinit { deinit {
mutex.lock() mutex.lock()
readyToWrite = false readyToWrite = false