merge master

This commit is contained in:
Erik 2016-06-17 10:16:52 -04:00
commit be76a1607c
17 changed files with 562 additions and 460 deletions

View File

@ -88,7 +88,7 @@ Carthage
-----------------
Add this line to your `Cartfile`:
```
github "socketio/socket.io-client-swift" ~> 6.1.2 # Or latest version
github "socketio/socket.io-client-swift" ~> 6.1.3 # Or latest version
```
Run `carthage update --platform ios,macosx`.
@ -102,7 +102,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Socket.IO-Client-Swift', '~> 6.1.2' # Or latest version
pod 'Socket.IO-Client-Swift', '~> 6.1.3' # Or latest version
```
Install pods:
@ -130,7 +130,7 @@ CocoaSeeds
Add this line to your `Seedfile`:
```
github "socketio/socket.io-client-swift", "v6.1.2", :files => "Source/*.swift" # Or latest version
github "socketio/socket.io-client-swift", "v6.1.3", :files => "Source/*.swift" # Or latest version
```
Run `seed install`.
@ -165,6 +165,7 @@ case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (inf
case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
case Secure(Bool) // If the connection should use TLS. Default is false.
case Security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL (Don't do this, iOS will yell at you)
case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
```

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIOClientSwift"
s.version = "6.1.2"
s.version = "6.1.3"
s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC
Socket.IO-client for iOS and OS X.
@ -14,7 +14,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v6.1.2' }
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v6.1.3' }
s.source_files = "Source/**/*.swift"
s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -69,9 +69,7 @@
74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
@ -87,6 +85,15 @@
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */; };
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
CEBA56961CDA0B7700BA0389 /* NSCharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */; };
@ -126,7 +133,6 @@
572EF21D1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-iOS.h"; sourceTree = "<group>"; };
572EF2241B51F16C00EEBB58 /* SocketIO-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
572EF22A1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF2381B51F18A00EEBB58 /* SocketIOClientSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIOClientSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
572EF23B1B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-Mac.h"; sourceTree = "<group>"; };
@ -151,7 +157,6 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketParsable.swift; path = Source/SocketParsable.swift; sourceTree = "<group>"; };
74171E5F1C10CD240062D398 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketStringReader.swift; path = Source/SocketStringReader.swift; sourceTree = "<group>"; };
74171E601C10CD240062D398 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = Source/SocketTypes.swift; sourceTree = "<group>"; };
74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; };
742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = "<group>"; };
@ -160,6 +165,9 @@
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; };
7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = "<group>"; };
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket/WebSocket.swift; sourceTree = "<group>"; };
74B4AD201D09A6190062A523 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/WebSocket/SSLSecurity.swift; sourceTree = "<group>"; };
74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientManager.swift; path = Source/SocketClientManager.swift; sourceTree = "<group>"; };
74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = "<group>"; };
CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSCharacterSet.swift; path = Source/NSCharacterSet.swift; sourceTree = "<group>"; };
CEBA56991CDA0B8200BA0389 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Source/String.swift; sourceTree = "<group>"; };
@ -219,7 +227,6 @@
children = (
5764DF7B1B51F24A004FF46E /* Source */,
572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */,
572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */,
572EF2391B51F18A00EEBB58 /* SocketIO-Mac */,
572EF2461B51F18A00EEBB58 /* SocketIO-MacTests */,
572EF21A1B51F16C00EEBB58 /* Products */,
@ -256,22 +263,6 @@
name = "Supporting Files";
sourceTree = "<group>";
};
572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */ = {
isa = PBXGroup;
children = (
572EF2291B51F16C00EEBB58 /* Supporting Files */,
);
path = "SocketIO-iOSTests";
sourceTree = "<group>";
};
572EF2291B51F16C00EEBB58 /* Supporting Files */ = {
isa = PBXGroup;
children = (
572EF22A1B51F16C00EEBB58 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
572EF2391B51F18A00EEBB58 /* SocketIO-Mac */ = {
isa = PBXGroup;
children = (
@ -315,10 +306,10 @@
5764DF7B1B51F24A004FF46E /* Source */ = {
isa = PBXGroup;
children = (
CEBA569E1CDA0C0C00BA0389 /* utils */,
74171E501C10CD240062D398 /* SocketAckEmitter.swift */,
74171E511C10CD240062D398 /* SocketAckManager.swift */,
74171E521C10CD240062D398 /* SocketAnyEvent.swift */,
74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */,
74171E531C10CD240062D398 /* SocketEngine.swift */,
74171E541C10CD240062D398 /* SocketEngineClient.swift */,
74171E551C10CD240062D398 /* SocketEnginePacketType.swift */,
@ -335,11 +326,21 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */,
74171E5F1C10CD240062D398 /* SocketStringReader.swift */,
74171E601C10CD240062D398 /* SocketTypes.swift */,
74171E621C10CD240062D398 /* WebSocket.swift */,
CEBA569E1CDA0C0C00BA0389 /* utils */,
74B4AD1B1D09A5C30062A523 /* Websocket */,
);
name = Source;
sourceTree = "<group>";
};
74B4AD1B1D09A5C30062A523 /* Websocket */ = {
isa = PBXGroup;
children = (
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */,
74B4AD201D09A6190062A523 /* SSLSecurity.swift */,
);
name = Websocket;
sourceTree = "<group>";
};
CEBA569E1CDA0C0C00BA0389 /* utils */ = {
isa = PBXGroup;
children = (
@ -595,6 +596,7 @@
740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */,
74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
CEBA569A1CDA0B8200BA0389 /* String.swift in Sources */,
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */,
74171E751C10CD240062D398 /* SocketEngine.swift in Sources */,
74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */,
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
@ -602,18 +604,19 @@
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */,
74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */,
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EBD1C10CD240062D398 /* SocketStringReader.swift in Sources */,
74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */,
74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */,
74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */,
74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -622,6 +625,15 @@
buildActionMask = 2147483647;
files = (
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */,
74171EA61C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E881C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171EA01C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E701C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC41C10CD240062D398 /* SocketTypes.swift in Sources */,
74171E8E1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7C1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171E821C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171EB21C10CD240062D398 /* SocketPacket.swift in Sources */,
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */,
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */,
7472C65C1BCAB53E003CA70D /* SocketNamespacePacketTest.swift in Sources */,
@ -632,6 +644,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */,
740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEA1C39926300364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569B1CDA0B8200BA0389 /* String.swift in Sources */,
@ -648,12 +661,13 @@
74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */,
74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -676,6 +690,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */,
740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEB1C39926C00364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569C1CDA0B8200BA0389 /* String.swift in Sources */,
@ -692,12 +707,13 @@
74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */,
74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -706,6 +722,15 @@
buildActionMask = 2147483647;
files = (
57634A231BD9B46D00E19CD7 /* SocketSideEffectTest.swift in Sources */,
74171EAA1C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E8C1C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171EA41C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E741C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */,
74171E921C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E801C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171E861C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171EB61C10CD240062D398 /* SocketPacket.swift in Sources */,
57634A2A1BD9B46D00E19CD7 /* SocketEngineTest.swift in Sources */,
57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */,
57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */,

View File

@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework"
BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference>
@ -57,7 +57,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework"
BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference>
@ -79,7 +79,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework"
BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference>
@ -97,7 +97,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework"
BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference>

View File

@ -44,4 +44,10 @@
[self.socket off:@"test"];
}
- (void)testSocketManager {
SocketClientManager* manager = [SocketClientManager sharedManager];
[manager addSocket:self.socket labeledAs:@"test"];
[manager removeSocketWithLabel:@"test"];
}
@end

View File

@ -13,7 +13,7 @@ class SocketParserTest: XCTestCase {
let testSocket = SocketIOClient(socketURL: NSURL())
//Format key: message; namespace-data-binary-id
static let packetTypes: Dictionary<String, (String, [AnyObject], [NSData], Int)> = [
static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
"25[\"test\"]": ("/", ["test"], [], 5),
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),

View File

@ -153,4 +153,16 @@ class SocketSideEffectTest: XCTestCase {
socket.parseBinaryData(data2)
waitForExpectationsWithTimeout(3, handler: nil)
}
func testSocketManager() {
let manager = SocketClientManager.sharedManager
manager["test"] = socket
XCTAssert(manager["test"] === socket, "failed to get socket")
manager["test"] = nil
XCTAssert(manager["test"] == nil, "socket not removed")
}
}

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,82 @@
//
// SocketClientManager.swift
// Socket.IO-Client-Swift
//
// Created by Erik Little on 6/11/16.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/**
Experimental socket manager.
API subject to change.
Can be used to persist sockets across ViewControllers.
Sockets are strongly stored, so be sure to remove them once they are no
longer needed.
Example usage:
```
let manager = SocketClientManager.sharedManager
manager["room1"] = socket1
manager["room2"] = socket2
manager.removeSocket(socket: socket2)
manager["room1"]?.emit("hello")
```
*/
public final class SocketClientManager : NSObject {
public static let sharedManager = SocketClientManager()
private var sockets = [String: SocketIOClient]()
public subscript(string: String) -> SocketIOClient? {
get {
return sockets[string]
}
set(socket) {
sockets[string] = socket
}
}
public func addSocket(socket: SocketIOClient, labeledAs label: String) {
sockets[label] = socket
}
public func removeSocket(withLabel label: String) -> SocketIOClient? {
return sockets.removeValueForKey(label)
}
public func removeSocket(socket socket: SocketIOClient) -> SocketIOClient? {
var returnSocket: SocketIOClient?
for (label, dictSocket) in sockets where dictSocket === socket {
returnSocket = sockets.removeValueForKey(label)
}
return returnSocket
}
public func removeSockets() {
sockets.removeAll()
}
}

View File

@ -76,6 +76,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue()
private var secure = false
private var security: SSLSecurity?
private var selfSigned = false
private var voipEnabled = false
@ -105,6 +106,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
voipEnabled = enable
case let .Secure(secure):
self.secure = secure
case let .Security(security):
self.security = security
case let .SelfSigned(selfSigned):
self.selfSigned = selfSigned
default:
@ -153,9 +156,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
// binary in base64 string
let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex]
if let data = NSData(base64EncodedString: noPrefix,
options: .IgnoreUnknownCharacters) {
client?.parseEngineBinaryData(data)
if let data = NSData(base64EncodedString: noPrefix, options: .IgnoreUnknownCharacters) {
client?.parseEngineBinaryData(data)
}
return true
@ -164,6 +166,16 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
}
}
private func closeOutEngine() {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
}
/// Starts the connection to the server
public func connect() {
if connected {
@ -171,7 +183,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
disconnect("reconnect")
}
DefaultSocketLogger.Logger.log("Starting engine", type: logType)
DefaultSocketLogger.Logger.log("Starting engine. Server: %@", type: logType, args: url)
DefaultSocketLogger.Logger.log("Handshaking", type: logType)
resetEngine()
@ -256,6 +268,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
ws?.voipEnabled = voipEnabled
ws?.delegate = self
ws?.selfSignedSSL = selfSigned
ws?.security = security
ws?.connect()
}
@ -267,35 +280,32 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
}
public func disconnect(reason: String) {
func postSendClose(data: NSData?, _ res: NSURLResponse?, _ err: NSError?) {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
client?.engineDidClose(reason)
}
guard connected else { return closeOutEngine() }
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if closed {
closeOutEngine()
client?.engineDidClose(reason)
return
}
if websocket {
sendWebSocketMessage("", withType: .Close, withData: [])
postSendClose(nil, nil, nil)
closeOutEngine()
} else {
// We need to take special care when we're polling that we send it ASAP
// Also make sure we're on the emitQueue since we're touching postWait
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(req, withCallback: postSendClose)
}
disconnectPolling()
}
}
// We need to take special care when we're polling that we send it ASAP
// Also make sure we're on the emitQueue since we're touching postWait
private func disconnectPolling() {
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(req) {_, _, _ in }
self.closeOutEngine()
}
}
@ -335,7 +345,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
guard let ws = self.ws else { return }
for msg in postWait {
ws.writeString(fixDoubleUTF8(msg))
ws.writeString(msg)
}
postWait.removeAll(keepCapacity: true)
@ -535,13 +545,11 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
connected = false
websocket = false
let reason = error?.localizedDescription ?? "Socket Disconnected"
if error != nil {
if let reason = error?.localizedDescription {
didError(reason)
} else {
client?.engineDidClose("Socket Disconnected")
}
client?.engineDidClose(reason)
} else {
flushProbeWait()
}

View File

@ -123,7 +123,7 @@ extension SocketEnginePollable {
return
}
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
@ -213,9 +213,7 @@ extension SocketEnginePollable {
fixedMessage = message
}
let strMsg = "\(type.rawValue)\(fixedMessage)"
postWait.append(strMsg)
postWait.append(String(type.rawValue) + fixedMessage)
for data in datas {
if case let .Right(bin) = createBinaryDataForSend(data) {

View File

@ -197,8 +197,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
handleEvent("disconnect", data: [reason], isInternalMessage: true)
}
/// Disconnects the socket. Only reconnect the same socket if you know what you're doing.
/// Will turn off automatic reconnects.
/// Disconnects the socket.
public func disconnect() {
assert(status != .NotConnected, "Tried closing a NotConnected client")

View File

@ -45,6 +45,7 @@ public enum SocketIOClientOption : ClientOption {
case ReconnectAttempts(Int)
case ReconnectWait(Int)
case Secure(Bool)
case Security(SSLSecurity)
case SelfSigned(Bool)
case SessionDelegate(NSURLSessionDelegate)
case VoipEnabled(Bool)
@ -85,6 +86,8 @@ public enum SocketIOClientOption : ClientOption {
description = "reconnectWait"
case .Secure:
description = "secure"
case .Security:
description = "security"
case .SelfSigned:
description = "selfSigned"
case .SessionDelegate:
@ -136,6 +139,8 @@ public enum SocketIOClientOption : ClientOption {
value = wait
case let .Secure(secure):
value = secure
case let .Security(security):
value = security
case let .SelfSigned(signed):
value = signed
case let .SessionDelegate(delegate):
@ -195,6 +200,8 @@ extension NSDictionary {
return .ReconnectWait(wait)
case let ("secure", secure as Bool):
return .Secure(secure)
case let ("security", security as SSLSecurity):
return .Security(security)
case let ("selfSigned", selfSigned as Bool):
return .SelfSigned(selfSigned)
case let ("sessionDelegate", delegate as NSURLSessionDelegate):

View File

@ -111,73 +111,31 @@ struct SocketPacket {
return message + restOfMessage
}
private func createAck() -> String {
let message: String
if type == .Ack {
if nsp == "/" {
message = "3\(id)"
} else {
message = "3\(nsp),\(id)"
}
} else {
if nsp == "/" {
message = "6\(binary.count)-\(id)"
} else {
message = "6\(binary.count)-\(nsp),\(id)"
}
}
return completeMessage(message)
}
private func createMessageForEvent() -> String {
let message: String
if type == .Event {
if nsp == "/" {
if id == -1 {
message = "2"
} else {
message = "2\(id)"
}
} else {
if id == -1 {
message = "2\(nsp),"
} else {
message = "2\(nsp),\(id)"
}
}
} else {
if nsp == "/" {
if id == -1 {
message = "5\(binary.count)-"
} else {
message = "5\(binary.count)-\(id)"
}
} else {
if id == -1 {
message = "5\(binary.count)-\(nsp),"
} else {
message = "5\(binary.count)-\(nsp),\(id)"
}
}
}
return completeMessage(message)
}
private func createPacketString() -> String {
let str: String
let typeString = String(type.rawValue)
let binaryCountString: String
let nspString: String
let idString: String
if type == .Event || type == .BinaryEvent {
str = createMessageForEvent()
if type == .BinaryEvent || type == .BinaryAck {
binaryCountString = typeString + String(binary.count) + "-"
} else {
str = createAck()
binaryCountString = typeString
}
return str
if nsp != "/" {
nspString = binaryCountString + nsp + ","
} else {
nspString = binaryCountString
}
if id != -1 {
idString = nspString + String(id)
} else {
idString = nspString
}
return completeMessage(idString)
}
// Called when we have all the binary data for a packet

View File

@ -96,14 +96,14 @@ extension SocketParsable {
if type == .Error {
parser.advanceIndexBy(-1)
}
while parser.hasNext && type != .Error {
if let int = Int(parser.read(1)) {
idString += String(int)
} else {
parser.advanceIndexBy(-2)
break
} else {
while parser.hasNext {
if let int = Int(parser.read(1)) {
idString += String(int)
} else {
parser.advanceIndexBy(-2)
break
}
}
}
@ -162,9 +162,7 @@ extension SocketParsable {
}
// Should execute event?
guard waitingPackets[waitingPackets.count - 1].addData(data) else {
return
}
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return }
let packet = waitingPackets.removeLast()

View File

@ -0,0 +1,259 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// SSLSecurity.swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import Security
public class SSLCert : NSObject {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class SSLSecurity : NSObject {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- 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
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: 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
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
super.init()
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain)!
} else {
policy = SecPolicyCreateBasicX509()!
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust,&result)
if result == .Unspecified || result == .Proceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509()!)
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- 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
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy!) {
keys.append(key)
}
return keys
}
return keys
}
}

View File

@ -23,6 +23,10 @@ import Foundation
import CoreFoundation
import Security
public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification"
public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification"
public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName"
public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
@ -35,7 +39,7 @@ public protocol WebSocketPongDelegate: class {
}
public class WebSocket : NSObject, NSStreamDelegate {
enum OpCode : UInt8 {
case ContinueFrame = 0x0
case TextFrame = 0x1
@ -46,7 +50,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
case Pong = 0xA
//B-F reserved.
}
public enum CloseCode : UInt16 {
case Normal = 1000
case GoingAway = 1001
@ -59,17 +63,17 @@ public class WebSocket : NSObject, NSStreamDelegate {
case PolicyViolated = 1008
case MessageTooBig = 1009
}
public static let ErrorDomain = "WebSocket"
enum InternalErrorCode : UInt16 {
// 0-999 WebSocket status codes not used
case OutputStreamWriteError = 1
}
//Where the callback is executed. It defaults to the main UI thread queue.
public var queue = dispatch_get_main_queue()
var optionalProtocols : [String]?
//Constant Values.
let headerWSUpgradeName = "Upgrade"
@ -90,7 +94,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
let MaskMask: UInt8 = 0x80
let PayloadLenMask: UInt8 = 0x7F
let MaxFrameSize: Int = 32
class WSResponse {
var isFin = false
var code: OpCode = .ContinueFrame
@ -98,7 +102,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
var frameCount = 0
var buffer: NSMutableData?
}
public weak var delegate: WebSocketDelegate?
public weak var pongDelegate: WebSocketPongDelegate?
public var onConnect: ((Void) -> Void)?
@ -130,6 +134,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
private var didDisconnect = false
private var readyToWrite = false
private let mutex = NSLock()
private let notificationCenter = NSNotificationCenter.defaultCenter()
private var canDispatch: Bool {
mutex.lock()
let canWork = readyToWrite
@ -138,7 +143,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
//the shared processing queue used for all websocket
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
//used for setting protocols.
public init(url: NSURL, protocols: [String]? = nil) {
self.url = url
@ -146,7 +151,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
writeQueue.maxConcurrentOperationCount = 1
optionalProtocols = protocols
}
///Connect to the websocket server on a background thread
public func connect() {
guard !isCreated else { return }
@ -155,14 +160,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
createHTTPRequest()
isCreated = false
}
/**
Disconnect from the server. I send a Close control frame to the server, then expect the server to respond with a Close control frame and close the socket from its end. I notify my delegate once the socket has been closed.
If you supply a non-nil `forceTimeout`, I wait at most that long (in seconds) for the server to close the socket. After the timeout expires, I close the socket and notify my delegate.
If you supply a zero (or negative) `forceTimeout`, I immediately close the socket (without sending a Close control frame) and notify my delegate.
- Parameter forceTimeout: Maximum time to wait for the server to close the socket.
*/
public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) {
@ -174,17 +179,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
fallthrough
case .None:
writeError(CloseCode.Normal.rawValue)
default:
self.disconnectStream(nil)
break
}
}
/**
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.
- parameter str: The string to write.
- parameter completion: The (optional) completion handler.
*/
@ -192,11 +198,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard isConnected else { return }
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion)
}
/**
Write binary data to the websocket. This sends it as a binary frame.
If you supply a non-nil completion block, I will perform it when the write completes.
- parameter data: The data to write.
- parameter completion: The (optional) completion handler.
*/
@ -204,20 +211,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard isConnected else { return }
dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion)
}
//write a ping to the websocket. This sends it as a control frame.
//yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
public func writePing(data: NSData, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(data, code: .Ping, writeCompletion: completion)
}
//private method that starts the connection
private func createHTTPRequest() {
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
url, kCFHTTPVersion1_1).takeRetainedValue()
var port = url.port
if port == nil {
if let scheme = url.scheme where ["wss", "https"].contains(scheme) {
@ -245,12 +252,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
initStreamsWithData(serializedRequest, Int(port!))
}
}
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
}
//generate a websocket key as needed in rfc
private func generateWebSocketKey() -> String {
var key = ""
@ -263,12 +270,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
return baseKey!
}
//Start the stream connection and write the data to the output stream
private func initStreamsWithData(data: NSData, _ port: Int) {
//higher level API we will cut over to at some point
//NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream)
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
let h: NSString = url.host!
@ -314,11 +321,11 @@ public class WebSocket : NSObject, NSStreamDelegate {
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
inStream.open()
outStream.open()
self.mutex.lock()
self.readyToWrite = true
self.mutex.unlock()
let bytes = UnsafePointer<UInt8>(data.bytes)
var out = timeout * 1000000 //wait 5 seconds before giving up
writeQueue.addOperationWithBlock { [weak self] in
@ -338,7 +345,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
//delegate for the stream methods. Processes incoming bytes
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
if let trust: AnyObject = possibleTrust {
@ -372,7 +379,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
cleanupStream()
doDisconnect(error)
}
private func cleanupStream() {
outputStream?.delegate = nil
inputStream?.delegate = nil
@ -387,13 +394,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
outputStream = nil
inputStream = nil
}
///handles the incoming bytes and sending them to the proper processing method
private func processInputStream() {
let buf = NSMutableData(capacity: BUFFER_MAX)
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
guard length > 0 else { return }
var process = false
if inputQueue.count == 0 {
@ -425,7 +432,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
inputQueue = inputQueue.filter{$0 != data}
}
}
//handle checking the inital connection status
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
let code = processHTTP(buffer, bufferLen: bufferLen)
@ -437,6 +444,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return }
s.onConnect?()
s.delegate?.websocketDidConnect(s)
s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self)
}
case -1:
fragBuffer = NSData(bytes: buffer, length: bufferLen)
@ -475,7 +483,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
return -1 //was unable to find the full TCP header
}
///validates the HTTP is a 101 as per the RFC spec
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
@ -494,12 +502,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
return -1
}
///read a 16 bit big endian value from a buffer
private static func readUint16(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt16 {
return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1])
}
///read a 64 bit big endian value from a buffer
private static func readUint64(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt64 {
var value = UInt64(0)
@ -508,20 +516,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
return value
}
///write a 16 bit big endian value to a buffer
private static func writeUint16(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt16) {
buffer[offset + 0] = UInt8(value >> 8)
buffer[offset + 1] = UInt8(value & 0xff)
}
///write a 64 bit big endian value to a buffer
private static func writeUint64(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt64) {
for i in 0...7 {
buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff)
}
}
/// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process.
@warn_unused_result
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
@ -676,12 +684,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
processResponse(response)
}
let step = Int(offset+numericCast(len))
return buffer.fromOffset(step)
}
}
/// Process all messages in the buffer if possible.
private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
@ -692,7 +700,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
fragBuffer = NSData(buffer: buffer)
}
}
///process the finished response of a buffer
private func processResponse(response: WSResponse) -> Bool {
if response.isFin && response.bytesLeft <= 0 {
@ -727,14 +735,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
return false
}
///Create an error
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
var details = [String: String]()
details[NSLocalizedDescriptionKey] = detail
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
}
///write a an error to the socket
private func writeError(code: UInt16) {
let buf = NSMutableData(capacity: sizeof(UInt16))
@ -768,7 +776,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32)
for i in 0..<dataLength {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
offset += 1
@ -797,14 +805,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
callback()
}
}
break
}
}
}
}
///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) {
guard !didDisconnect else { return }
@ -815,269 +823,34 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return }
s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(s, error: error)
let userInfo = error.map({ [WebsocketDisconnectionErrorKeyName: $0] })
s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
}
}
deinit {
mutex.lock()
readyToWrite = false
mutex.unlock()
cleanupStream()
}
}
private extension NSData {
convenience init(buffer: UnsafeBufferPointer<UInt8>) {
self.init(bytes: buffer.baseAddress, length: buffer.count)
}
}
private extension UnsafeBufferPointer {
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset)
}
}
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
public class SSLCert {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class SSLSecurity {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- 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
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: 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
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
guard let aPolicy = SecPolicyCreateSSL(true, domain) else { return false }
policy = aPolicy
} else {
guard let aPolicy = SecPolicyCreateBasicX509() else { return false }
policy = aPolicy
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust,&result)
if result == .Unspecified || result == .Proceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
guard let policy = SecPolicyCreateBasicX509() else { return nil }
return extractPublicKeyFromCert(cert, policy: policy)
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = .Invalid
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- 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
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
guard let policy = SecPolicyCreateBasicX509() else { return [] }
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
keys.append(key)
}
return keys
}
return keys
}
}