From b3ea49f7fd612624f21ce2caeccf570f572f9eeb Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 8 Jul 2017 10:12:20 -0400 Subject: [PATCH] Make Starscream a dependency --- .gitmodules | 4 + Package.swift | 3 +- Socket.IO-Client-Swift.podspec | 16 +- .../project.pbxproj | 191 ++- Source/Compression.swift | 174 --- Source/SSLSecurity.swift | 259 ---- Source/SocketEngine.swift | 1 + Source/SocketEngineSpec.swift | 1 + Source/SocketEngineWebsocket.swift | 1 + Source/SocketExtensions.swift | 1 + Source/SocketIOClientOption.swift | 1 + Source/Starscream | 1 + Source/WebSocket.swift | 1107 ----------------- zlib/include.h | 2 - zlib/module.modulemap | 9 - 15 files changed, 162 insertions(+), 1609 deletions(-) create mode 100644 .gitmodules delete mode 100644 Source/Compression.swift delete mode 100644 Source/SSLSecurity.swift create mode 160000 Source/Starscream delete mode 100644 Source/WebSocket.swift delete mode 100644 zlib/include.h delete mode 100644 zlib/module.modulemap diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..66ee071 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "Source/Starscream"] + path = Source/Starscream + url = https://github.com/nuclearace/Starscream + branch = socket.io diff --git a/Package.swift b/Package.swift index b2a9ea6..28de957 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,6 @@ import PackageDescription let package = Package( name: "SocketIO", dependencies: [ - .Package(url: "https://github.com/daltoniam/zlib-spm.git", majorVersion: 1), - .Package(url: "https://github.com/IBM-Swift/CommonCrypto.git", majorVersion: 0), + .Package(url: "https://github.com/nuclearace/Starscream", majorVersion: 8), ] ) diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index 9db34f8..77aed2a 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" s.module_name = "SocketIO" - s.version = "10.2.0" + s.version = "11.0.0" s.summary = "Socket.IO-client for iOS and OS X" s.description = <<-DESC Socket.IO-client for iOS and OS X. @@ -14,14 +14,12 @@ 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 => 'v10.2.0' } - s.source_files = "Source/**/*.swift" - s.libraries = 'z' - s.preserve_paths = 'zlib/*' s.requires_arc = true - s.pod_target_xcconfig = { - 'SWIFT_VERSION' => '3.1', - 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Socket.IO-Client-Swift/zlib' + s.source = { + :git => "https://github.com/socketio/socket.io-client-swift.git", + :tag => 'v11.0.0', + :submodules => true } - # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files + s.source_files = "Source/*.swift" + s.dependency "StarscreamSocketIO", "~> 8.0.0" end diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 5ec5af5..0a79938 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -89,26 +89,14 @@ 747BC59A1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */; }; 747BC59B1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */; }; 747BC59F1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC59E1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift */; }; - 749642B51D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; - 749642B61D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; - 749642B71D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; - 749642B81D3FCE5500DD32D1 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B41D3FCE5500DD32D1 /* WebSocket.swift */; }; - 749642B91D3FCE5500DD32D1 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B41D3FCE5500DD32D1 /* WebSocket.swift */; }; - 749642BA1D3FCE5500DD32D1 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B41D3FCE5500DD32D1 /* WebSocket.swift */; }; 74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketIOClientSpec.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 */; }; - 74DA216E1F0943EE009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; }; - 74DA216F1F0943F4009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; }; - 74DA21701F0943F8009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; }; 74DA21721F094408009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21711F094408009C19EE /* libz.tbd */; }; 74DA21741F09440F009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; }; 74DA21761F094417009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21751F094417009C19EE /* libz.tbd */; }; 74DA217C1F09457B009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; }; - 74DA21811F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; }; - 74DA21821F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; }; - 74DA21831F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; }; 74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; }; 74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; }; CEBA569A1CDA0B8200BA0389 /* SocketExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56991CDA0B8200BA0389 /* SocketExtensions.swift */; }; @@ -138,6 +126,69 @@ remoteGlobalIDString = 576349FA1BD9B46A00E19CD7; remoteInfo = "SocketIO-tvOS"; }; + 745225EC1F1BA89E007EA874 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 6B3E79E519D48B7F006071F7; + remoteInfo = "Starscream iOS"; + }; + 74638B631F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B3E79E619D48B7F006071F7; + remoteInfo = "Starscream iOS"; + }; + 74638B651F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B3E79F119D48B7F006071F7; + remoteInfo = "Starscream iOSTests"; + }; + 74638B671F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D9C3E35F19E48FF1009FC285; + remoteInfo = "Starscream OSX"; + }; + 74638B691F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D9C3E36919E48FF1009FC285; + remoteInfo = "Starscream OSXTests"; + }; + 74638B6B1F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277971BD673A70003036D; + remoteInfo = "Starscream tvOS"; + }; + 74638B6D1F111CD000F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277A01BD673A70003036D; + remoteInfo = "Starscream tvOSTests"; + }; + 74638B711F111CF100F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D9C3E35E19E48FF1009FC285; + remoteInfo = "Starscream OSX"; + }; + 74638B731F111CF600F5E1FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 091277961BD673A70003036D; + remoteInfo = "Starscream tvOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -177,21 +228,17 @@ 742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = ""; }; 74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketAckManagerTest.swift; sourceTree = ""; }; 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketParserTest.swift; sourceTree = ""; }; + 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Starscream.xcodeproj; path = Source/Starscream/Starscream.xcodeproj; sourceTree = ""; }; 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = ""; }; 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = ""; }; 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientConfiguration.swift; path = Source/SocketIOClientConfiguration.swift; sourceTree = ""; }; 747BC59E1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = ""; }; - 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/SSLSecurity.swift; sourceTree = ""; }; - 749642B41D3FCE5500DD32D1 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = ""; }; 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = ""; }; 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientManager.swift; path = Source/SocketClientManager.swift; sourceTree = ""; }; - 74DA216C1F09438D009C19EE /* include.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = include.h; path = zlib/include.h; sourceTree = ""; }; 74DA21711F094408009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; 74DA21731F09440F009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 74DA21751F094417009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; - 74DA21771F09444E009C19EE /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; name = module.modulemap; path = zlib/module.modulemap; sourceTree = ""; }; 74DA217D1F0945E9009C19EE /* libcommonCrypto.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcommonCrypto.tbd; path = usr/lib/system/libcommonCrypto.tbd; sourceTree = SDKROOT; }; - 74DA21801F094887009C19EE /* Compression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compression.swift; path = Source/Compression.swift; sourceTree = ""; }; 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = ""; }; CEBA56991CDA0B8200BA0389 /* SocketExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketExtensions.swift; path = Source/SocketExtensions.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -255,7 +302,7 @@ 572EF20D1B51F12F00EEBB58 = { isa = PBXGroup; children = ( - 74DA216B1F094371009C19EE /* zlib */, + 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */, 6CA08A9B1D615C190061FD2A /* Frameworks */, 572EF21A1B51F16C00EEBB58 /* Products */, 572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */, @@ -361,7 +408,6 @@ 74171E5E1C10CD240062D398 /* SocketParsable.swift */, 74171E5F1C10CD240062D398 /* SocketStringReader.swift */, 74171E601C10CD240062D398 /* SocketTypes.swift */, - 74B4AD1B1D09A5C30062A523 /* Websocket */, ); name = Source; sourceTree = ""; @@ -404,23 +450,17 @@ name = tvOS; sourceTree = ""; }; - 74B4AD1B1D09A5C30062A523 /* Websocket */ = { + 74638B5B1F111CD000F5E1FF /* Products */ = { isa = PBXGroup; children = ( - 74DA21801F094887009C19EE /* Compression.swift */, - 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */, - 749642B41D3FCE5500DD32D1 /* WebSocket.swift */, + 74638B641F111CD000F5E1FF /* StarscreamSocketIO.framework */, + 74638B661F111CD000F5E1FF /* Starscream iOSTests.xctest */, + 74638B681F111CD000F5E1FF /* StarscreamSocketIO.framework */, + 74638B6A1F111CD000F5E1FF /* Starscream OSXTests.xctest */, + 74638B6C1F111CD000F5E1FF /* StarscreamSocketIO.framework */, + 74638B6E1F111CD000F5E1FF /* Starscream tvOSTests.xctest */, ); - name = Websocket; - sourceTree = ""; - }; - 74DA216B1F094371009C19EE /* zlib */ = { - isa = PBXGroup; - children = ( - 74DA216C1F09438D009C19EE /* include.h */, - 74DA21771F09444E009C19EE /* module.modulemap */, - ); - name = zlib; + name = Products; sourceTree = ""; }; /* End PBXGroup section */ @@ -431,7 +471,6 @@ buildActionMask = 2147483647; files = ( 572EF21F1B51F16C00EEBB58 /* SocketIO.h in Headers */, - 74DA21701F0943F8009C19EE /* include.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -440,7 +479,6 @@ buildActionMask = 2147483647; files = ( 572EF23D1B51F18A00EEBB58 /* SocketIO-Mac.h in Headers */, - 74DA216F1F0943F4009C19EE /* include.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -449,7 +487,6 @@ buildActionMask = 2147483647; files = ( 57634A111BD9B46A00E19CD7 /* SocketIO.h in Headers */, - 74DA216E1F0943EE009C19EE /* include.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -468,6 +505,7 @@ buildRules = ( ); dependencies = ( + 745225ED1F1BA89E007EA874 /* PBXTargetDependency */, ); name = "SocketIO-iOS"; productName = "SocketIO-iOS"; @@ -504,6 +542,7 @@ buildRules = ( ); dependencies = ( + 74638B721F111CF100F5E1FF /* PBXTargetDependency */, ); name = "SocketIO-Mac"; productName = "SocketIO-Mac"; @@ -540,6 +579,7 @@ buildRules = ( ); dependencies = ( + 74638B741F111CF600F5E1FF /* PBXTargetDependency */, ); name = "SocketIO-tvOS"; productName = "SocketIO-iOS"; @@ -600,6 +640,12 @@ mainGroup = 572EF20D1B51F12F00EEBB58; productRefGroup = 572EF21A1B51F16C00EEBB58 /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 74638B5B1F111CD000F5E1FF /* Products */; + ProjectRef = 74638B5A1F111CD000F5E1FF /* Starscream.xcodeproj */; + }, + ); projectRoot = ""; targets = ( 572EF2181B51F16C00EEBB58 /* SocketIO-iOS */, @@ -612,6 +658,51 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + 74638B641F111CD000F5E1FF /* StarscreamSocketIO.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = StarscreamSocketIO.framework; + remoteRef = 74638B631F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 74638B661F111CD000F5E1FF /* Starscream iOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Starscream iOSTests.xctest"; + remoteRef = 74638B651F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 74638B681F111CD000F5E1FF /* StarscreamSocketIO.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = StarscreamSocketIO.framework; + remoteRef = 74638B671F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 74638B6A1F111CD000F5E1FF /* Starscream OSXTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Starscream OSXTests.xctest"; + remoteRef = 74638B691F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 74638B6C1F111CD000F5E1FF /* StarscreamSocketIO.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = StarscreamSocketIO.framework; + remoteRef = 74638B6B1F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 74638B6E1F111CD000F5E1FF /* Starscream tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Starscream tvOSTests.xctest"; + remoteRef = 74638B6D1F111CD000F5E1FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 572EF2171B51F16C00EEBB58 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -675,14 +766,11 @@ 74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */, 74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */, 74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */, - 749642B81D3FCE5500DD32D1 /* WebSocket.swift in Sources */, 74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */, - 749642B51D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */, 74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, - 74DA21811F094887009C19EE /* Compression.swift in Sources */, 74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 747BC5991D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */, @@ -718,14 +806,11 @@ 74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */, 74171EAD1C10CD240062D398 /* SocketLogger.swift in Sources */, 74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */, - 749642B91D3FCE5500DD32D1 /* WebSocket.swift in Sources */, 74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */, - 749642B61D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */, 74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, - 74DA21821F094887009C19EE /* Compression.swift in Sources */, 74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 747BC59A1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */, @@ -765,14 +850,11 @@ 74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */, 74171EAF1C10CD240062D398 /* SocketLogger.swift in Sources */, 74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */, - 749642BA1D3FCE5500DD32D1 /* WebSocket.swift in Sources */, 74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */, - 749642B71D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */, 74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, - 74DA21831F094887009C19EE /* Compression.swift in Sources */, 74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 747BC59B1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */, @@ -809,6 +891,21 @@ target = 576349FA1BD9B46A00E19CD7 /* SocketIO-tvOS */; targetProxy = 57634A3D1BD9B4B800E19CD7 /* PBXContainerItemProxy */; }; + 745225ED1F1BA89E007EA874 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Starscream iOS"; + targetProxy = 745225EC1F1BA89E007EA874 /* PBXContainerItemProxy */; + }; + 74638B721F111CF100F5E1FF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Starscream OSX"; + targetProxy = 74638B711F111CF100F5E1FF /* PBXContainerItemProxy */; + }; + 74638B741F111CF600F5E1FF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Starscream tvOS"; + targetProxy = 74638B731F111CF600F5E1FF /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1141,7 +1238,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_INCLUDE_PATHS = $SRCROOT/zlib; + SWIFT_INCLUDE_PATHS = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; @@ -1199,7 +1296,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_INCLUDE_PATHS = $SRCROOT/zlib; + SWIFT_INCLUDE_PATHS = ""; SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/Source/Compression.swift b/Source/Compression.swift deleted file mode 100644 index 36f1978..0000000 --- a/Source/Compression.swift +++ /dev/null @@ -1,174 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Compression.swift -// -// Created by Joseph Ross on 7/16/14. -// Copyright © 2017 Joseph Ross. -// -// 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. -// -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Compression implementation is implemented in conformance with RFC 7692 Compression Extensions -// for WebSocket: https://tools.ietf.org/html/rfc7692 -// -////////////////////////////////////////////////////////////////////////////////////////////////// -import Foundation -import CZLib - -class Decompressor { - private var strm = z_stream() - private var buffer = [UInt8](repeating: 0, count: 0x2000) - private var inflateInitialized = false - private let windowBits:Int - - init?(windowBits:Int) { - self.windowBits = windowBits - guard initInflate() else { return nil } - } - - private func initInflate() -> Bool { - if Z_OK == inflateInit2_(&strm, -CInt(windowBits), - ZLIB_VERSION, CInt(MemoryLayout.size)) - { - inflateInitialized = true - return true - } - return false - } - - func reset() throws { - teardownInflate() - guard initInflate() else { throw NSError() } - } - - func decompress(_ data: Data, finish: Bool) throws -> Data { - return try data.withUnsafeBytes { (bytes:UnsafePointer) -> Data in - return try decompress(bytes: bytes, count: data.count, finish: finish) - } - } - - func decompress(bytes: UnsafePointer, count: Int, finish: Bool) throws -> Data { - var decompressed = Data() - try decompress(bytes: bytes, count: count, out: &decompressed) - - if finish { - let tail:[UInt8] = [0x00, 0x00, 0xFF, 0xFF] - try decompress(bytes: tail, count: tail.count, out: &decompressed) - } - - return decompressed - - } - - private func decompress(bytes: UnsafePointer, count: Int, out:inout Data) throws { - var res:CInt = 0 - strm.next_in = UnsafeMutablePointer(mutating: bytes) - strm.avail_in = CUnsignedInt(count) - - repeat { - strm.next_out = UnsafeMutablePointer(&buffer) - strm.avail_out = CUnsignedInt(buffer.count) - - res = inflate(&strm, 0) - - let byteCount = buffer.count - Int(strm.avail_out) - out.append(buffer, count: byteCount) - } while res == Z_OK && strm.avail_out == 0 - - guard (res == Z_OK && strm.avail_out > 0) - || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) - else { - throw NSError(domain: WebSocket.ErrorDomain, code: Int(WebSocket.InternalErrorCode.compressionError.rawValue), userInfo: nil) - } - } - - private func teardownInflate() { - if inflateInitialized, Z_OK == inflateEnd(&strm) { - inflateInitialized = false - } - } - - deinit { - teardownInflate() - } -} - -class Compressor { - private var strm = z_stream() - private var buffer = [UInt8](repeating: 0, count: 0x2000) - private var deflateInitialized = false - private let windowBits:Int - - init?(windowBits: Int) { - self.windowBits = windowBits - guard initDeflate() else { return nil } - } - - private func initDeflate() -> Bool { - if Z_OK == deflateInit2_(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - -CInt(windowBits), 8, Z_DEFAULT_STRATEGY, - ZLIB_VERSION, CInt(MemoryLayout.size)) - { - deflateInitialized = true - return true - } - return false - } - - func reset() throws { - teardownDeflate() - guard initDeflate() else { throw NSError() } - } - - func compress(_ data: Data) throws -> Data { - var compressed = Data() - var res:CInt = 0 - data.withUnsafeBytes { (ptr:UnsafePointer) -> Void in - strm.next_in = UnsafeMutablePointer(mutating: ptr) - strm.avail_in = CUnsignedInt(data.count) - - repeat { - strm.next_out = UnsafeMutablePointer(&buffer) - strm.avail_out = CUnsignedInt(buffer.count) - - res = deflate(&strm, Z_SYNC_FLUSH) - - let byteCount = buffer.count - Int(strm.avail_out) - compressed.append(buffer, count: byteCount) - } - while res == Z_OK && strm.avail_out == 0 - - } - - guard res == Z_OK && strm.avail_out > 0 - || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) - else { - throw NSError(domain: WebSocket.ErrorDomain, code: Int(WebSocket.InternalErrorCode.compressionError.rawValue), userInfo: nil) - } - - compressed.removeLast(4) - return compressed - } - - private func teardownDeflate() { - if deflateInitialized, Z_OK == deflateEnd(&strm) { - deflateInitialized = false - } - } - - deinit { - teardownDeflate() - } -} diff --git a/Source/SSLSecurity.swift b/Source/SSLSecurity.swift deleted file mode 100644 index 4d2c60a..0000000 --- a/Source/SSLSecurity.swift +++ /dev/null @@ -1,259 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// SSLSecurity.swift -// Starscream -// -// Created by Dalton Cherry on 5/16/15. -// Copyright (c) 2014-2016 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 protocol SSLTrustValidator { - func isValid(_ trust: SecTrust, domain: String?) -> Bool -} - -open class SSLCert : NSObject { - var certData: Data? - var key: SecKey? - - /** - 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: Data) { - 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: SecKey) { - self.key = key - } -} - -open class SSLSecurity : SSLTrustValidator { - public var validatedDN = true //should the domain name be validated? - - var isReady = false //is the key processing done? - var certificates: [Data]? //the certificates - @nonobjc var pubKeys: [SecKey]? //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 = Bundle.main.paths(forResourcesOfType: "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 as Data)) - } - return certs - } - - self.init(certs: certs, usePublicKeys: usePublicKeys) - } - - /** - Designated init - - - 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 - - - returns: a representation security object to be used with - */ - public init(certs: [SSLCert], usePublicKeys: Bool) { - self.usePublicKeys = usePublicKeys - - if self.usePublicKeys { - DispatchQueue.global(qos: .default).async { - let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in - var pubKeys = pubKeys - if let data = cert.certData, 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([Data]()) { (certificates: [Data], cert: SSLCert) -> [Data] 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: SecTrust, 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: SecPolicy - if self.validatedDN { - policy = SecPolicyCreateSSL(true, domain as NSString?) - } else { - policy = SecPolicyCreateBasicX509() - } - SecTrustSetPolicies(trust,policy) - if self.usePublicKeys { - if let keys = self.pubKeys { - let serverPubKeys = publicKeyChain(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 = certificateChain(trust) - var collect = [SecCertificate]() - for cert in certs { - collect.append(SecCertificateCreateWithData(nil,cert as CFData)!) - } - SecTrustSetAnchorCertificates(trust,collect as NSArray) - var result: SecTrustResultType = .unspecified - 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: Data) -> SecKey? { - guard let cert = SecCertificateCreateWithData(nil, data as CFData) else { return nil } - - return extractPublicKey(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 extractPublicKey(_ cert: SecCertificate, policy: SecPolicy) -> SecKey? { - var possibleTrust: SecTrust? - SecTrustCreateWithCertificates(cert, policy, &possibleTrust) - - guard let trust = possibleTrust else { return nil } - var result: SecTrustResultType = .unspecified - 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 certificateChain(_ trust: SecTrust) -> [Data] { - let certificates = (0.. [Data] in - var certificates = certificates - let cert = SecTrustGetCertificateAtIndex(trust, index) - certificates.append(SecCertificateCopyData(cert!) as Data) - 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 - */ - @nonobjc func publicKeyChain(_ trust: SecTrust) -> [SecKey] { - let policy = SecPolicyCreateBasicX509() - let keys = (0.. [SecKey] in - var keys = keys - let cert = SecTrustGetCertificateAtIndex(trust, index) - if let key = extractPublicKey(cert!, policy: policy) { - keys.append(key) - } - - return keys - } - - return keys - } - - -} diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 6d7ae5e..3d93979 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -24,6 +24,7 @@ import Dispatch import Foundation +import StarscreamSocketIO /// The class that handles the engine.io protocol and transports. /// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods. diff --git a/Source/SocketEngineSpec.swift b/Source/SocketEngineSpec.swift index fe5d781..ab8f10d 100644 --- a/Source/SocketEngineSpec.swift +++ b/Source/SocketEngineSpec.swift @@ -24,6 +24,7 @@ // import Foundation +import StarscreamSocketIO /// Specifies a SocketEngine. @objc public protocol SocketEngineSpec { diff --git a/Source/SocketEngineWebsocket.swift b/Source/SocketEngineWebsocket.swift index 3a76a9d..70b885c 100644 --- a/Source/SocketEngineWebsocket.swift +++ b/Source/SocketEngineWebsocket.swift @@ -24,6 +24,7 @@ // import Foundation +import StarscreamSocketIO /// Protocol that is used to implement socket.io WebSocket support public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate { diff --git a/Source/SocketExtensions.swift b/Source/SocketExtensions.swift index fc15ad5..ef422c3 100644 --- a/Source/SocketExtensions.swift +++ b/Source/SocketExtensions.swift @@ -23,6 +23,7 @@ // THE SOFTWARE. import Foundation +import StarscreamSocketIO enum JSONError : Error { case notArray diff --git a/Source/SocketIOClientOption.swift b/Source/SocketIOClientOption.swift index 9ab5cc7..e258bbd 100644 --- a/Source/SocketIOClientOption.swift +++ b/Source/SocketIOClientOption.swift @@ -23,6 +23,7 @@ // THE SOFTWARE. import Foundation +import StarscreamSocketIO protocol ClientOption : CustomStringConvertible, Equatable { func getSocketIOOptionValue() -> Any diff --git a/Source/Starscream b/Source/Starscream new file mode 160000 index 0000000..eb7f7da --- /dev/null +++ b/Source/Starscream @@ -0,0 +1 @@ +Subproject commit eb7f7da02ef367433d9d282e82add1c7321a8780 diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift deleted file mode 100644 index 38a25bd..0000000 --- a/Source/WebSocket.swift +++ /dev/null @@ -1,1107 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Websocket.swift -// -// Created by Dalton Cherry on 7/16/14. -// Copyright (c) 2014-2016 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 CoreFoundation -import CommonCrypto - -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?) - func websocketDidReceiveMessage(socket: WebSocket, text: String) - func websocketDidReceiveData(socket: WebSocket, data: Data) -} - -public protocol WebSocketPongDelegate: class { - func websocketDidReceivePong(socket: WebSocket, data: Data?) -} - -// A Delegate with more advanced info on messages and connection etc. -public protocol WebSocketAdvancedDelegate: class { - func websocketDidConnect(socket: WebSocket) - func websocketDidDisconnect(socket: WebSocket, error: NSError?) - func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse) - func websocketDidReceiveData(socket: WebSocket, data: Data, response: WebSocket.WSResponse) - func websocketHttpUpgrade(socket: WebSocket, request: CFHTTPMessage) - func websocketHttpUpgrade(socket: WebSocket, response: CFHTTPMessage) -} - -open class WebSocket : NSObject, StreamDelegate { - - public enum OpCode : UInt8 { - case continueFrame = 0x0 - case textFrame = 0x1 - case binaryFrame = 0x2 - // 3-7 are reserved. - case connectionClose = 0x8 - case ping = 0x9 - case pong = 0xA - // B-F reserved. - } - - public enum CloseCode : UInt16 { - case normal = 1000 - case goingAway = 1001 - case protocolError = 1002 - case protocolUnhandledType = 1003 - // 1004 reserved. - case noStatusReceived = 1005 - //1006 reserved. - case encoding = 1007 - case policyViolated = 1008 - case messageTooBig = 1009 - } - - public static let ErrorDomain = "WebSocket" - - enum InternalErrorCode: UInt16 { - // 0-999 WebSocket status codes not used - case outputStreamWriteError = 1 - case compressionError = 2 - case invalidSSLError = 3 - case writeTimeoutError = 4 - } - - // Where the callback is executed. It defaults to the main UI thread queue. - public var callbackQueue = DispatchQueue.main - - var optionalProtocols: [String]? - - // MARK: - Constants - let headerWSUpgradeName = "Upgrade" - let headerWSUpgradeValue = "websocket" - let headerWSHostName = "Host" - let headerWSConnectionName = "Connection" - let headerWSConnectionValue = "Upgrade" - let headerWSProtocolName = "Sec-WebSocket-Protocol" - let headerWSVersionName = "Sec-WebSocket-Version" - let headerWSVersionValue = "13" - let headerWSExtensionName = "Sec-WebSocket-Extensions" - let headerWSKeyName = "Sec-WebSocket-Key" - let headerOriginName = "Origin" - let headerWSAcceptName = "Sec-WebSocket-Accept" - let BUFFER_MAX = 4096 - let FinMask: UInt8 = 0x80 - let OpCodeMask: UInt8 = 0x0F - let RSVMask: UInt8 = 0x70 - let RSV1Mask: UInt8 = 0x40 - let MaskMask: UInt8 = 0x80 - let PayloadLenMask: UInt8 = 0x7F - let MaxFrameSize: Int = 32 - let httpSwitchProtocolCode = 101 - let supportedSSLSchemes = ["wss", "https"] - - public class WSResponse { - var isFin = false - public var code: OpCode = .continueFrame - var bytesLeft = 0 - public var frameCount = 0 - public var buffer: NSMutableData? - public let firstFrame = { - return Date() - }() - } - - // MARK: - Delegates - /// Responds to callback about new messages coming in over the WebSocket - /// and also connection/disconnect messages. - public weak var delegate: WebSocketDelegate? - - /// The optional advanced delegate can be used insteadof of the delegate - public weak var advancedDelegate: WebSocketAdvancedDelegate? - - /// Receives a callback for each pong message recived. - public weak var pongDelegate: WebSocketPongDelegate? - - - // MARK: - Block based API. - public enum HTTPMethod { - case get - case post - case put - case connect - case custom(value: String) - var representation: String { - switch self { - case .get: - return "GET" - case .post: - return "POST" - case .put: - return "PUT" - case .connect: - return "CONNECT" - case .custom(let value): - return value.capitalized - } - } - } - - public var onConnect: (() -> Void)? - public var onDisconnect: ((NSError?) -> Void)? - public var onText: ((String) -> Void)? - public var onData: ((Data) -> Void)? - public var onPong: ((Data?) -> Void)? - - public var httpMethod: HTTPMethod = .get - public var headers = [String: String]() - public var disableSSLCertValidation = false - public var enableCompression = true - public var security: SSLTrustValidator? - public var enabledSSLCipherSuites: [SSLCipherSuite]? - public var origin: String? - public var timeout = 5 - public var isConnected: Bool { - return connected - } - - public var currentURL: URL { return url } - - // MARK: - Private - - private struct CompressionState { - var supportsCompression = false - var messageNeedsDecompression = false - var serverMaxWindowBits = 15 - var clientMaxWindowBits = 15 - var clientNoContextTakeover = false - var serverNoContextTakeover = false - var decompressor:Decompressor? = nil - var compressor:Compressor? = nil - } - - private var url: URL - private var inputStream: InputStream? - private var outputStream: OutputStream? - private var connected = false - private var isConnecting = false - private var compressionState = CompressionState() - private var writeQueue = OperationQueue() - private var readStack = [WSResponse]() - private var inputQueue = [Data]() - private var fragBuffer: Data? - private var certValidated = false - private var didDisconnect = false - private var readyToWrite = false - private var headerSecKey = "" - private let mutex = NSLock() - private let notificationCenter = NotificationCenter.default - private var canDispatch: Bool { - mutex.lock() - let canWork = readyToWrite - mutex.unlock() - return canWork - } - /// The shared processing queue used for all WebSocket. - private static let sharedWorkQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: []) - - /// Used for setting protocols. - public init(url: URL, protocols: [String]? = nil) { - self.url = url - self.origin = url.absoluteString - if let hostUrl = URL (string: "/", relativeTo: url) { - var origin = hostUrl.absoluteString - origin.remove(at: origin.index(before: origin.endIndex)) - self.origin = origin - } - writeQueue.maxConcurrentOperationCount = 1 - optionalProtocols = protocols - } - - // Used for specifically setting the QOS for the write queue. - public convenience init(url: URL, writeQueueQOS: QualityOfService, protocols: [String]? = nil) { - self.init(url: url, protocols: protocols) - writeQueue.qualityOfService = writeQueueQOS - } - - /** - Connect to the WebSocket server on a background thread. - */ - open func connect() { - guard !isConnecting else { return } - didDisconnect = false - isConnecting = true - createHTTPRequest() - } - - /** - 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. - - Parameter closeCode: The code to send on disconnect. The default is the normal close code for cleanly disconnecting a webSocket. - */ - open func disconnect(forceTimeout: TimeInterval? = nil, closeCode: UInt16 = CloseCode.normal.rawValue) { - guard isConnected else { return } - switch forceTimeout { - case .some(let seconds) where seconds > 0: - let milliseconds = Int(seconds * 1_000) - callbackQueue.asyncAfter(deadline: .now() + .milliseconds(milliseconds)) { [weak self] in - self?.disconnectStream(nil) - } - fallthrough - case .none: - writeError(closeCode) - default: - 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 string: The string to write. - - parameter completion: The (optional) completion handler. - */ - open func write(string: String, completion: (() -> ())? = nil) { - guard isConnected else { return } - dequeueWrite(string.data(using: String.Encoding.utf8)!, 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. - */ - open func write(data: Data, completion: (() -> ())? = nil) { - 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 - */ - open func write(ping: Data, completion: (() -> ())? = nil) { - guard isConnected else { return } - dequeueWrite(ping, code: .ping, writeCompletion: completion) - } - - /** - Private method that starts the connection. - */ - private func createHTTPRequest() { - let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpMethod.representation as CFString, - url as CFURL, kCFHTTPVersion1_1).takeRetainedValue() - - var port = url.port - if port == nil { - if supportedSSLSchemes.contains(url.scheme!) { - port = 443 - } else { - port = 80 - } - } - addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue) - addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue) - if let protocols = optionalProtocols { - addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joined(separator: ",")) - } - headerSecKey = generateWebSocketKey() - addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue) - addHeader(urlRequest, key: headerWSKeyName, val: headerSecKey) - if let origin = origin { - addHeader(urlRequest, key: headerOriginName, val: origin) - } - if enableCompression { - let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15" - addHeader(urlRequest, key: headerWSExtensionName, val: val) - } - addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") - for (key, value) in headers { - addHeader(urlRequest, key: key, val: value) - } - if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) { - let serializedRequest = cfHTTPMessage.takeRetainedValue() - initStreamsWithData(serializedRequest as Data, Int(port!)) - self.advancedDelegate?.websocketHttpUpgrade(socket: self, request: urlRequest) - } - } - - /** - Add a header to the CFHTTPMessage by using the NSString bridges to CFString - */ - private func addHeader(_ urlRequest: CFHTTPMessage, key: String, val: String) { - CFHTTPMessageSetHeaderFieldValue(urlRequest, key as CFString, val as CFString) - } - - /** - Generate a WebSocket key as needed in RFC. - */ - private func generateWebSocketKey() -> String { - var key = "" - let seed = 16 - for _ in 0..? - var writeStream: Unmanaged? - let h = url.host! as NSString - CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream) - inputStream = readStream!.takeRetainedValue() - outputStream = writeStream!.takeRetainedValue() - guard let inStream = inputStream, let outStream = outputStream else { return } - inStream.delegate = self - outStream.delegate = self - if supportedSSLSchemes.contains(url.scheme!) { - certValidated = false - inStream.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 { - certValidated = true //not a https session, so no need to check SSL pinning - } - - CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue) - CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) - inStream.open() - outStream.open() - - self.mutex.lock() - self.readyToWrite = true - self.mutex.unlock() - - let bytes = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self) - var out = timeout * 1_000_000 // wait 5 seconds before giving up - let operation = BlockOperation() - operation.addExecutionBlock { [weak self, weak operation] in - guard let sOperation = operation else { return } - while !outStream.hasSpaceAvailable && !sOperation.isCancelled { - usleep(100) // wait until the socket is ready - guard !sOperation.isCancelled else { return } - out -= 100 - if out < 0 { - WebSocket.sharedWorkQueue.async { - self?.cleanupStream() - } - let errCode = InternalErrorCode.writeTimeoutError.rawValue - self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: errCode)) - return - } else if outStream.streamError != nil { - return // disconnectStream will be called. - } - } - guard !sOperation.isCancelled, let s = self else { return } - // Do the pinning now if needed - if let sec = s.security, !s.certValidated { - if let possibleTrust = outStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) { - let domain = outStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String - s.certValidated = sec.isValid(possibleTrust as! SecTrust, domain: domain) - } else { - s.certValidated = false - } - if !s.certValidated { - WebSocket.sharedWorkQueue.async { - let errCode = InternalErrorCode.invalidSSLError.rawValue - let error = s.errorWithDetail("Invalid SSL certificate", code: errCode) - s.disconnectStream(error) - } - return - } - } - outStream.write(bytes, maxLength: data.count) - } - writeQueue.addOperation(operation) - } - - /** - Delegate for the stream methods. Processes incoming bytes - */ - open func stream(_ aStream: Stream, handle eventCode: Stream.Event) { - if eventCode == .hasBytesAvailable { - if aStream == inputStream { - processInputStream() - } - } else if eventCode == .errorOccurred { - disconnectStream(aStream.streamError as NSError?) - } else if eventCode == .endEncountered { - disconnectStream(nil) - } - } - - /** - Disconnect the stream object and notifies the delegate. - */ - private func disconnectStream(_ error: NSError?, runDelegate: Bool = true) { - if error == nil { - writeQueue.waitUntilAllOperationsAreFinished() - } else { - writeQueue.cancelAllOperations() - } - cleanupStream() - connected = false - if runDelegate { - doDisconnect(error) - } - } - - /** - cleanup the streams. - */ - private func cleanupStream() { - outputStream?.delegate = nil - inputStream?.delegate = nil - if let stream = inputStream { - CFReadStreamSetDispatchQueue(stream, nil) - stream.close() - } - if let stream = outputStream { - CFWriteStreamSetDispatchQueue(stream, nil) - stream.close() - } - outputStream = nil - inputStream = nil - fragBuffer = nil - } - - /** - Handles the incoming bytes and sending them to the proper processing method. - */ - private func processInputStream() { - let buf = NSMutableData(capacity: BUFFER_MAX) - let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) - let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) - guard length > 0 else { return } - var process = false - if inputQueue.count == 0 { - process = true - } - inputQueue.append(Data(bytes: buffer, count: length)) - if process { - dequeueInput() - } - } - - /** - Dequeue the incoming input so it is processed in order. - */ - private func dequeueInput() { - while !inputQueue.isEmpty { - autoreleasepool { - let data = inputQueue[0] - var work = data - if let buffer = fragBuffer { - var combine = NSData(data: buffer) as Data - combine.append(data) - work = combine - fragBuffer = nil - } - let buffer = UnsafeRawPointer((work as NSData).bytes).assumingMemoryBound(to: UInt8.self) - let length = work.count - if !connected { - processTCPHandshake(buffer, bufferLen: length) - } else { - processRawMessagesInBuffer(buffer, bufferLen: length) - } - inputQueue = inputQueue.filter{ $0 != data } - } - } - } - - /** - Handle checking the inital connection status - */ - private func processTCPHandshake(_ buffer: UnsafePointer, bufferLen: Int) { - let code = processHTTP(buffer, bufferLen: bufferLen) - switch code { - case 0: - break - case -1: - fragBuffer = Data(bytes: buffer, count: bufferLen) - break // do nothing, we are going to collect more data - default: - doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code))) - } - } - - /** - Finds the HTTP Packet in the TCP stream, by looking for the CRLF. - */ - private func processHTTP(_ buffer: UnsafePointer, bufferLen: Int) -> Int { - let CRLFBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] - var k = 0 - var totalSize = 0 - for i in 0.. 0 { - let code = validateResponse(buffer, bufferLen: totalSize) - if code != 0 { - return code - } - isConnecting = false - connected = true - didDisconnect = false - if canDispatch { - callbackQueue.async { [weak self] in - guard let s = self else { return } - s.onConnect?() - s.delegate?.websocketDidConnect(socket: s) - s.advancedDelegate?.websocketDidConnect(socket: s) - s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self) - } - } - totalSize += 1 //skip the last \n - let restSize = bufferLen - totalSize - if restSize > 0 { - processRawMessagesInBuffer(buffer + totalSize, bufferLen: restSize) - } - return 0 //success - } - 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, bufferLen: Int) -> Int { - let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() - CFHTTPMessageAppendBytes(response, buffer, bufferLen) - let code = CFHTTPMessageGetResponseStatusCode(response) - self.advancedDelegate?.websocketHttpUpgrade(socket: self, response: response) - if code != httpSwitchProtocolCode { - return code - } - if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { - let headers = cfHeaders.takeRetainedValue() as NSDictionary - if let extensionHeader = headers[headerWSExtensionName as NSString] as? String { - processExtensionHeader(extensionHeader) - } - - if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString { - if acceptKey.length > 0 { - if headerSecKey.characters.count > 0 { - let sha = "\(headerSecKey)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64() - if sha != acceptKey as String { - return -1 - } - } - return 0 - } - } - } - return -1 - } - - /** - Parses the extension header, setting up the compression parameters. - */ - func processExtensionHeader(_ extensionHeader: String) { - let parts = extensionHeader.components(separatedBy: ";") - for p in parts { - let part = p.trimmingCharacters(in: .whitespaces) - if part == "permessage-deflate" { - compressionState.supportsCompression = true - } else if part.hasPrefix("server_max_window_bits=") { - let valString = part.components(separatedBy: "=")[1] - if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { - compressionState.serverMaxWindowBits = val - } - } else if part.hasPrefix("client_max_window_bits=") { - let valString = part.components(separatedBy: "=")[1] - if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { - compressionState.clientMaxWindowBits = val - } - } else if part == "client_no_context_takeover" { - compressionState.clientNoContextTakeover = true - } else if part == "server_no_context_takeover" { - compressionState.serverNoContextTakeover = true - } - } - if compressionState.supportsCompression { - compressionState.decompressor = Decompressor(windowBits: compressionState.serverMaxWindowBits) - compressionState.compressor = Compressor(windowBits: compressionState.clientMaxWindowBits) - } - } - - /** - Read a 16 bit big endian value from a buffer - */ - private static func readUint16(_ buffer: UnsafePointer, 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, offset: Int) -> UInt64 { - var value = UInt64(0) - for i in 0...7 { - value = (value << 8) | UInt64(buffer[offset + i]) - } - return value - } - - /** - Write a 16-bit big endian value to a buffer. - */ - private static func writeUint16(_ buffer: UnsafeMutablePointer, 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, 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. - */ - private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer) -> UnsafeBufferPointer { - let response = readStack.last - guard let baseAddress = buffer.baseAddress else {return emptyBuffer} - let bufferLen = buffer.count - if response != nil && bufferLen < 2 { - fragBuffer = Data(buffer: buffer) - return emptyBuffer - } - if let response = response, response.bytesLeft > 0 { - var len = response.bytesLeft - var extra = bufferLen - response.bytesLeft - if response.bytesLeft > bufferLen { - len = bufferLen - extra = 0 - } - response.bytesLeft -= len - response.buffer?.append(Data(bytes: baseAddress, count: len)) - _ = processResponse(response) - return buffer.fromOffset(bufferLen - extra) - } else { - let isFin = (FinMask & baseAddress[0]) - let receivedOpcodeRawValue = (OpCodeMask & baseAddress[0]) - let receivedOpcode = OpCode(rawValue: receivedOpcodeRawValue) - let isMasked = (MaskMask & baseAddress[1]) - let payloadLen = (PayloadLenMask & baseAddress[1]) - var offset = 2 - if compressionState.supportsCompression && receivedOpcode != .continueFrame { - compressionState.messageNeedsDecompression = (RSV1Mask & baseAddress[0]) > 0 - } - if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong && !compressionState.messageNeedsDecompression { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode)) - writeError(errCode) - return emptyBuffer - } - let isControlFrame = (receivedOpcode == .connectionClose || receivedOpcode == .ping) - if !isControlFrame && (receivedOpcode != .binaryFrame && receivedOpcode != .continueFrame && - receivedOpcode != .textFrame && receivedOpcode != .pong) { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcodeRawValue)", code: errCode)) - writeError(errCode) - return emptyBuffer - } - if isControlFrame && isFin == 0 { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode)) - writeError(errCode) - return emptyBuffer - } - var closeCode = CloseCode.normal.rawValue - if receivedOpcode == .connectionClose { - if payloadLen == 1 { - closeCode = CloseCode.protocolError.rawValue - } else if payloadLen > 1 { - closeCode = WebSocket.readUint16(baseAddress, offset: offset) - if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1011 && closeCode < 3000) { - closeCode = CloseCode.protocolError.rawValue - } - } - if payloadLen < 2 { - doDisconnect(errorWithDetail("connection closed by server", code: closeCode)) - writeError(closeCode) - return emptyBuffer - } - } else if isControlFrame && payloadLen > 125 { - writeError(CloseCode.protocolError.rawValue) - return emptyBuffer - } - var dataLength = UInt64(payloadLen) - if dataLength == 127 { - dataLength = WebSocket.readUint64(baseAddress, offset: offset) - offset += MemoryLayout.size - } else if dataLength == 126 { - dataLength = UInt64(WebSocket.readUint16(baseAddress, offset: offset)) - offset += MemoryLayout.size - } - if bufferLen < offset || UInt64(bufferLen - offset) < dataLength { - fragBuffer = Data(bytes: baseAddress, count: bufferLen) - return emptyBuffer - } - var len = dataLength - if dataLength > UInt64(bufferLen) { - len = UInt64(bufferLen-offset) - } - if receivedOpcode == .connectionClose && len > 0 { - let size = MemoryLayout.size - offset += size - len -= UInt64(size) - } - let data: Data - if compressionState.messageNeedsDecompression, let decompressor = compressionState.decompressor { - do { - data = try decompressor.decompress(bytes: baseAddress+offset, count: Int(len), finish: isFin > 0) - if isFin > 0 && compressionState.serverNoContextTakeover{ - try decompressor.reset() - } - } catch { - let closeReason = "Decompression failed: \(error)" - let closeCode = CloseCode.encoding.rawValue - doDisconnect(errorWithDetail(closeReason, code: closeCode)) - writeError(closeCode) - return emptyBuffer - } - } else { - 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 canDispatch { - callbackQueue.async { [weak self] in - guard let s = self else { return } - let pongData: Data? = data.count > 0 ? data : nil - s.onPong?(pongData) - s.pongDelegate?.websocketDidReceivePong(socket: s, data: pongData) - } - } - return buffer.fromOffset(offset + Int(len)) - } - var response = readStack.last - if isControlFrame { - response = nil // Don't append pings. - } - if isFin == 0 && receivedOpcode == .continueFrame && response == nil { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode)) - writeError(errCode) - return emptyBuffer - } - var isNew = false - if response == nil { - if receivedOpcode == .continueFrame { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("first frame can't be a continue frame", - code: errCode)) - writeError(errCode) - return emptyBuffer - } - isNew = true - response = WSResponse() - response!.code = receivedOpcode! - response!.bytesLeft = Int(dataLength) - response!.buffer = NSMutableData(data: data) - } else { - if receivedOpcode == .continueFrame { - response!.bytesLeft = Int(dataLength) - } else { - let errCode = CloseCode.protocolError.rawValue - doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame", - code: errCode)) - writeError(errCode) - return emptyBuffer - } - response!.buffer!.append(data) - } - if let response = response { - response.bytesLeft -= Int(len) - response.frameCount += 1 - response.isFin = isFin > 0 ? true : false - if isNew { - readStack.append(response) - } - _ = 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, bufferLen: Int) { - var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen) - repeat { - buffer = processOneRawMessage(inBuffer: buffer) - } while buffer.count >= 2 - if buffer.count > 0 { - fragBuffer = Data(buffer: buffer) - } - } - - /** - Process the finished response of a buffer. - */ - private func processResponse(_ response: WSResponse) -> Bool { - if response.isFin && response.bytesLeft <= 0 { - if response.code == .ping { - let data = response.buffer! // local copy so it is perverse for writing - dequeueWrite(data as Data, code: .pong) - } else if response.code == .textFrame { - let str: NSString? = NSString(data: response.buffer! as Data, encoding: String.Encoding.utf8.rawValue) - if str == nil { - writeError(CloseCode.encoding.rawValue) - return false - } - if canDispatch { - callbackQueue.async { [weak self] in - guard let s = self else { return } - s.onText?(str! as String) - s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String) - s.advancedDelegate?.websocketDidReceiveMessage(socket: s, text: str! as String, response: response) - } - } - } else if response.code == .binaryFrame { - if canDispatch { - let data = response.buffer! // local copy so it is perverse for writing - callbackQueue.async { [weak self] in - guard let s = self else { return } - s.onData?(data as Data) - s.delegate?.websocketDidReceiveData(socket: s, data: data as Data) - s.advancedDelegate?.websocketDidReceiveData(socket: s, data: data as Data, response: response) - } - } - } - readStack.removeLast() - return true - } - 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 an error to the socket - */ - private func writeError(_ code: UInt16) { - let buf = NSMutableData(capacity: MemoryLayout.size) - let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) - WebSocket.writeUint16(buffer, offset: 0, value: code) - dequeueWrite(Data(bytes: buffer, count: MemoryLayout.size), code: .connectionClose) - } - - /** - Used to write things to the stream - */ - private func dequeueWrite(_ data: Data, code: OpCode, writeCompletion: (() -> ())? = nil) { - let operation = BlockOperation() - operation.addExecutionBlock { [weak self, weak operation] in - //stream isn't ready, let's wait - guard let s = self else { return } - guard let sOperation = operation else { return } - var offset = 2 - var firstByte:UInt8 = s.FinMask | code.rawValue - var data = data - if [.textFrame, .binaryFrame].contains(code), let compressor = s.compressionState.compressor { - do { - data = try compressor.compress(data) - if s.compressionState.clientNoContextTakeover { - try compressor.reset() - } - firstByte |= s.RSV1Mask - } catch { - // TODO: report error? We can just send the uncompressed frame. - } - } - let dataLength = data.count - let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize) - let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self) - buffer[0] = firstByte - if dataLength < 126 { - buffer[1] = CUnsignedChar(dataLength) - } else if dataLength <= Int(UInt16.max) { - buffer[1] = 126 - WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength)) - offset += MemoryLayout.size - } else { - buffer[1] = 127 - WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength)) - offset += MemoryLayout.size - } - buffer[1] |= s.MaskMask - let maskKey = UnsafeMutablePointer(buffer + offset) - _ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout.size), maskKey) - offset += MemoryLayout.size - - for i in 0...size] - offset += 1 - } - var total = 0 - while !sOperation.isCancelled { - guard let outStream = s.outputStream else { break } - let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self) - let len = outStream.write(writeBuffer, maxLength: offset-total) - if len < 0 { - var error: Error? - if let streamError = outStream.streamError { - error = streamError - } else { - let errCode = InternalErrorCode.outputStreamWriteError.rawValue - error = s.errorWithDetail("output stream error during write", code: errCode) - } - s.doDisconnect(error as NSError?) - break - } else { - total += len - } - if total >= offset { - if let queue = self?.callbackQueue, let callback = writeCompletion { - queue.async { - callback() - } - } - - break - } - } - } - writeQueue.addOperation(operation) - } - - /** - Used to preform the disconnect delegate - */ - private func doDisconnect(_ error: NSError?) { - guard !didDisconnect else { return } - didDisconnect = true - isConnecting = false - connected = false - guard canDispatch else {return} - callbackQueue.async { [weak self] in - guard let s = self else { return } - s.onDisconnect?(error) - s.delegate?.websocketDidDisconnect(socket: s, error: error) - s.advancedDelegate?.websocketDidDisconnect(socket: s, error: error) - let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] } - s.notificationCenter.post(name: NSNotification.Name(WebsocketDidDisconnectNotification), object: self, userInfo: userInfo) - } - } - - // MARK: - Deinit - deinit { - mutex.lock() - readyToWrite = false - mutex.unlock() - cleanupStream() - writeQueue.cancelAllOperations() - } - -} - -private extension String { - func sha1Base64() -> String { - let data = self.data(using: String.Encoding.utf8)! - var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) - data.withUnsafeBytes { _ = CC_SHA1($0, CC_LONG(data.count), &digest) } - return Data(bytes: digest).base64EncodedString() - } -} - -private extension Data { - - init(buffer: UnsafeBufferPointer) { - self.init(bytes: buffer.baseAddress!, count: buffer.count) - } - -} - -private extension UnsafeBufferPointer { - - func fromOffset(_ offset: Int) -> UnsafeBufferPointer { - return UnsafeBufferPointer(start: baseAddress?.advanced(by: offset), count: count - offset) - } - -} - -private let emptyBuffer = UnsafeBufferPointer(start: nil, count: 0) diff --git a/zlib/include.h b/zlib/include.h deleted file mode 100644 index cb3747f..0000000 --- a/zlib/include.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -#include diff --git a/zlib/module.modulemap b/zlib/module.modulemap deleted file mode 100644 index 1b2abee..0000000 --- a/zlib/module.modulemap +++ /dev/null @@ -1,9 +0,0 @@ -module CZLib [system] { - header "include.h" - link "z" - export * -} -module CommonCrypto [system] { - header "include.h" - export * -}