├── README.md └── WebSocketDemo ├── Podfile ├── Podfile.lock ├── Pods ├── Headers │ ├── Private │ │ └── SocketRocket │ │ │ ├── SRWebSocket.h │ │ │ └── SocketRocket.h │ └── Public │ │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ └── SocketRocket.h ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── sunjun.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-WebSocketDemo.xcscheme │ │ ├── SocketRocket.xcscheme │ │ └── xcschememanagement.plist ├── SocketRocket │ ├── LICENSE │ ├── README.rst │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ ├── SRWebSocket.m │ │ └── SocketRocket.h └── Target Support Files │ ├── Pods-WebSocketDemo │ ├── Pods-WebSocketDemo-acknowledgements.markdown │ ├── Pods-WebSocketDemo-acknowledgements.plist │ ├── Pods-WebSocketDemo-dummy.m │ ├── Pods-WebSocketDemo-frameworks.sh │ ├── Pods-WebSocketDemo-resources.sh │ ├── Pods-WebSocketDemo.debug.xcconfig │ └── Pods-WebSocketDemo.release.xcconfig │ └── SocketRocket │ ├── SocketRocket-dummy.m │ ├── SocketRocket-prefix.pch │ └── SocketRocket.xcconfig ├── WebSocketDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── sunjun.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── sunjun.xcuserdatad │ └── xcschemes │ ├── WebSocketDemo.xcscheme │ └── xcschememanagement.plist ├── WebSocketDemo.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── sunjun.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── WebSocketDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── WebSocket │ ├── SocketRocketUtility.h │ └── SocketRocketUtility.m └── main.m ├── WebSocketDemoTests ├── Info.plist └── WebSocketDemoTests.m └── WebSocketDemoUITests ├── Info.plist └── WebSocketDemoUITests.m /README.md: -------------------------------------------------------------------------------- 1 | # SJWebSocketDemo 2 | 3 | 使用SocketRocket 进行websocket长连接 4 | 5 | 我使用的是pod管理库 所以在podfile中加入 6 | pod 'SocketRocket' 7 | 8 | 在使用命令行工具cd到当前工程 安装 9 | pod install 10 | 11 | 12 | 打开使用 13 | 14 | ` 15 | [[SocketRocketUtility instance] SRWebSocketOpenWithUrlStrig:@"写入自己后台的地址"]; 16 | ` 17 | 18 | 关闭使用 19 | 20 | ` 21 | [[SocketRocketUtility instance] SRWebSocketClose]; 22 | ` 23 | 24 | 打开成功会收到通知需要在前面先监听 25 | 26 | ` 27 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidOpen) name:kWebSocketDidOpenNote object:nil]; 28 | ` 29 | 30 | 收到服务器发送的消息会收到通知需要在前面先监听 31 | 32 | ` 33 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidReceiveMsg:) name:kWebSocketDidCloseNote object:nil]; 34 | ` 35 | 36 | 好用的话不要吝啬你的star哦 :) 37 | 38 | -------------------------------------------------------------------------------- /WebSocketDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, ‘8.0’ 2 | target “WebSocketDemo” do 3 | pod 'SocketRocket' 4 | end 5 | -------------------------------------------------------------------------------- /WebSocketDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SocketRocket (0.5.1) 3 | 4 | DEPENDENCIES: 5 | - SocketRocket 6 | 7 | SPEC CHECKSUMS: 8 | SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 9 | 10 | PODFILE CHECKSUM: db7b709a4ce242b5da7af3cdde892d7b0e76f60d 11 | 12 | COCOAPODS: 1.1.0 13 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Headers/Private/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Headers/Private/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Headers/Public/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Headers/Public/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SocketRocket (0.5.1) 3 | 4 | DEPENDENCIES: 5 | - SocketRocket 6 | 7 | SPEC CHECKSUMS: 8 | SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 9 | 10 | PODFILE CHECKSUM: db7b709a4ce242b5da7af3cdde892d7b0e76f60d 11 | 12 | COCOAPODS: 1.1.0 13 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 183243B5DDAC603859A54026659BD14F /* SocketRocket-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */; }; 11 | 1B4F1D62654945DEBA7BAC4806DF74BD /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 34DA3DC37F9B59D2FC91BCC0A6A76FF3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */; }; 13 | 4A8581012207AD46884A1D45198FBBD7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */; }; 14 | 6A81C810E35E9E27E780C4E045F23D77 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 84F6C7AA0CD8DA406874CE4EEFDAB0FA /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */; }; 16 | 9FFEDDD51E5341D0263DECF57B64B0B0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */; }; 17 | C246AE68FFE36603AD0847764B6202BC /* Pods-WebSocketDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 128BA05987D8983FB38212285B98E5E8 /* Pods-WebSocketDemo-dummy.m */; }; 18 | E22767CF8B19FF997E5C21FFD6BDE7BE /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 5D3759B0005CFE70EA3D232C96EDDF2A /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 1B3FDED83149F7C943520562CB499E0D; 27 | remoteInfo = SocketRocket; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 04212E45A0D1BBCAD80D486B86C0B5D0 /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 0A0AD8922F95636F3B334AAD5933FF29 /* Pods-WebSocketDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-WebSocketDemo.debug.xcconfig"; sourceTree = ""; }; 34 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SRWebSocket.m; path = SocketRocket/SRWebSocket.m; sourceTree = ""; }; 35 | 128BA05987D8983FB38212285B98E5E8 /* Pods-WebSocketDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-WebSocketDemo-dummy.m"; sourceTree = ""; }; 36 | 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SocketRocket-dummy.m"; sourceTree = ""; }; 37 | 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; 38 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SRWebSocket.h; path = SocketRocket/SRWebSocket.h; sourceTree = ""; }; 39 | 5C789C6619BCCE48F8E1702EA4F05470 /* Pods-WebSocketDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-WebSocketDemo-acknowledgements.markdown"; sourceTree = ""; }; 40 | 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 41 | 81ED5606DF56484C11C9E852E586A23C /* SocketRocket-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-prefix.pch"; sourceTree = ""; }; 42 | 846323F13DA9C1B707762B79DE385D22 /* Pods-WebSocketDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-WebSocketDemo-resources.sh"; sourceTree = ""; }; 43 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 44 | AE3929C48C951722F79845F0F93DBE6A /* Pods-WebSocketDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-WebSocketDemo-acknowledgements.plist"; sourceTree = ""; }; 45 | BCE3890E0307AF9F0AAA6B1681E5F0C7 /* Pods-WebSocketDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-WebSocketDemo-frameworks.sh"; sourceTree = ""; }; 46 | D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SocketRocket.xcconfig; sourceTree = ""; }; 47 | D451D81EC77BBC55BDABA16C1166D442 /* libPods-WebSocketDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WebSocketDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SocketRocket.h; path = SocketRocket/SocketRocket.h; sourceTree = ""; }; 49 | EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 50 | FD9BF65DAFDCDEA223E495F668BA590E /* Pods-WebSocketDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-WebSocketDemo.release.xcconfig"; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 2E75E904A2D7650D81FDB34B3787DFD1 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | E22767CF8B19FF997E5C21FFD6BDE7BE /* CFNetwork.framework in Frameworks */, 59 | 9FFEDDD51E5341D0263DECF57B64B0B0 /* Foundation.framework in Frameworks */, 60 | 4A8581012207AD46884A1D45198FBBD7 /* Security.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | B19C317666F12180687860572200286A /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 34DA3DC37F9B59D2FC91BCC0A6A76FF3 /* Foundation.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 04310F8822AFFD42107F8DA169E0A47A /* Support Files */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */, 79 | 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */, 80 | 81ED5606DF56484C11C9E852E586A23C /* SocketRocket-prefix.pch */, 81 | ); 82 | name = "Support Files"; 83 | path = "../Target Support Files/SocketRocket"; 84 | sourceTree = ""; 85 | }; 86 | 1178F420F04662BB9B205EF659BBA2AB /* iOS */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */, 90 | EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */, 91 | 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */, 92 | ); 93 | name = iOS; 94 | sourceTree = ""; 95 | }; 96 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 1178F420F04662BB9B205EF659BBA2AB /* iOS */, 100 | ); 101 | name = Frameworks; 102 | sourceTree = ""; 103 | }; 104 | 15556009BF3AF0D5DFE34835D9CB6CE8 /* Targets Support Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | E95573A95741E75A23F86E466F6DC636 /* Pods-WebSocketDemo */, 108 | ); 109 | name = "Targets Support Files"; 110 | sourceTree = ""; 111 | }; 112 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */, 116 | ); 117 | name = Pods; 118 | sourceTree = ""; 119 | }; 120 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */, 124 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */, 125 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */, 126 | 04310F8822AFFD42107F8DA169E0A47A /* Support Files */, 127 | ); 128 | path = SocketRocket; 129 | sourceTree = ""; 130 | }; 131 | 7DB346D0F39D3F0E887471402A8071AB = { 132 | isa = PBXGroup; 133 | children = ( 134 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 135 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */, 136 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */, 137 | FFFE00D42C74CEC9375C2EBB7CA3E246 /* Products */, 138 | 15556009BF3AF0D5DFE34835D9CB6CE8 /* Targets Support Files */, 139 | ); 140 | sourceTree = ""; 141 | }; 142 | E95573A95741E75A23F86E466F6DC636 /* Pods-WebSocketDemo */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 5C789C6619BCCE48F8E1702EA4F05470 /* Pods-WebSocketDemo-acknowledgements.markdown */, 146 | AE3929C48C951722F79845F0F93DBE6A /* Pods-WebSocketDemo-acknowledgements.plist */, 147 | 128BA05987D8983FB38212285B98E5E8 /* Pods-WebSocketDemo-dummy.m */, 148 | BCE3890E0307AF9F0AAA6B1681E5F0C7 /* Pods-WebSocketDemo-frameworks.sh */, 149 | 846323F13DA9C1B707762B79DE385D22 /* Pods-WebSocketDemo-resources.sh */, 150 | 0A0AD8922F95636F3B334AAD5933FF29 /* Pods-WebSocketDemo.debug.xcconfig */, 151 | FD9BF65DAFDCDEA223E495F668BA590E /* Pods-WebSocketDemo.release.xcconfig */, 152 | ); 153 | name = "Pods-WebSocketDemo"; 154 | path = "Target Support Files/Pods-WebSocketDemo"; 155 | sourceTree = ""; 156 | }; 157 | FFFE00D42C74CEC9375C2EBB7CA3E246 /* Products */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | D451D81EC77BBC55BDABA16C1166D442 /* libPods-WebSocketDemo.a */, 161 | 04212E45A0D1BBCAD80D486B86C0B5D0 /* libSocketRocket.a */, 162 | ); 163 | name = Products; 164 | sourceTree = ""; 165 | }; 166 | /* End PBXGroup section */ 167 | 168 | /* Begin PBXHeadersBuildPhase section */ 169 | BB8EB456A581509F29CCE91F7C4CDBAD /* Headers */ = { 170 | isa = PBXHeadersBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 1B4F1D62654945DEBA7BAC4806DF74BD /* SocketRocket.h in Headers */, 174 | 6A81C810E35E9E27E780C4E045F23D77 /* SRWebSocket.h in Headers */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXHeadersBuildPhase section */ 179 | 180 | /* Begin PBXNativeTarget section */ 181 | 0C8D56DF53D25726A27E225824845096 /* Pods-WebSocketDemo */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 0C7560BE69FF7D88E973153B457CC00B /* Build configuration list for PBXNativeTarget "Pods-WebSocketDemo" */; 184 | buildPhases = ( 185 | EEDD3A5278C63BF352571FB5D7E81874 /* Sources */, 186 | B19C317666F12180687860572200286A /* Frameworks */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | 255EDC0D9580A894DDEC65D3F4D2F488 /* PBXTargetDependency */, 192 | ); 193 | name = "Pods-WebSocketDemo"; 194 | productName = "Pods-WebSocketDemo"; 195 | productReference = D451D81EC77BBC55BDABA16C1166D442 /* libPods-WebSocketDemo.a */; 196 | productType = "com.apple.product-type.library.static"; 197 | }; 198 | 1B3FDED83149F7C943520562CB499E0D /* SocketRocket */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = 8A05E5D1AB00659BE3ED9CA06F25AE77 /* Build configuration list for PBXNativeTarget "SocketRocket" */; 201 | buildPhases = ( 202 | 50588425D8260341D4DA9D228B33E0D1 /* Sources */, 203 | 2E75E904A2D7650D81FDB34B3787DFD1 /* Frameworks */, 204 | BB8EB456A581509F29CCE91F7C4CDBAD /* Headers */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | ); 210 | name = SocketRocket; 211 | productName = SocketRocket; 212 | productReference = 04212E45A0D1BBCAD80D486B86C0B5D0 /* libSocketRocket.a */; 213 | productType = "com.apple.product-type.library.static"; 214 | }; 215 | /* End PBXNativeTarget section */ 216 | 217 | /* Begin PBXProject section */ 218 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 219 | isa = PBXProject; 220 | attributes = { 221 | LastSwiftUpdateCheck = 0730; 222 | LastUpgradeCheck = 0700; 223 | }; 224 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 225 | compatibilityVersion = "Xcode 3.2"; 226 | developmentRegion = English; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | ); 231 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 232 | productRefGroup = FFFE00D42C74CEC9375C2EBB7CA3E246 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 0C8D56DF53D25726A27E225824845096 /* Pods-WebSocketDemo */, 237 | 1B3FDED83149F7C943520562CB499E0D /* SocketRocket */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXSourcesBuildPhase section */ 243 | 50588425D8260341D4DA9D228B33E0D1 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 183243B5DDAC603859A54026659BD14F /* SocketRocket-dummy.m in Sources */, 248 | 84F6C7AA0CD8DA406874CE4EEFDAB0FA /* SRWebSocket.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | EEDD3A5278C63BF352571FB5D7E81874 /* Sources */ = { 253 | isa = PBXSourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | C246AE68FFE36603AD0847764B6202BC /* Pods-WebSocketDemo-dummy.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXTargetDependency section */ 263 | 255EDC0D9580A894DDEC65D3F4D2F488 /* PBXTargetDependency */ = { 264 | isa = PBXTargetDependency; 265 | name = SocketRocket; 266 | target = 1B3FDED83149F7C943520562CB499E0D /* SocketRocket */; 267 | targetProxy = 5D3759B0005CFE70EA3D232C96EDDF2A /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 015A368F878AC3E2CEAE21DDE8026304 /* Debug */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_ANALYZER_NONNULL = YES; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_WARN_BOOL_CONVERSION = YES; 282 | CLANG_WARN_CONSTANT_CONVERSION = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INT_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | CODE_SIGNING_REQUIRED = NO; 291 | COPY_PHASE_STRIP = NO; 292 | ENABLE_TESTABILITY = YES; 293 | GCC_C_LANGUAGE_STANDARD = gnu99; 294 | GCC_DYNAMIC_NO_PIC = NO; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "POD_CONFIGURATION_DEBUG=1", 298 | "DEBUG=1", 299 | "$(inherited)", 300 | ); 301 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 309 | ONLY_ACTIVE_ARCH = YES; 310 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 311 | STRIP_INSTALLED_PRODUCT = NO; 312 | SYMROOT = "${SRCROOT}/../build"; 313 | }; 314 | name = Debug; 315 | }; 316 | 0B9BBDCE386CC40DB20E94BF4F654AC4 /* Debug */ = { 317 | isa = XCBuildConfiguration; 318 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 319 | buildSettings = { 320 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 321 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 322 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 323 | DEBUG_INFORMATION_FORMAT = dwarf; 324 | ENABLE_STRICT_OBJC_MSGSEND = YES; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 327 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 328 | MTL_ENABLE_DEBUG_INFO = YES; 329 | OTHER_LDFLAGS = ""; 330 | OTHER_LIBTOOLFLAGS = ""; 331 | PRIVATE_HEADERS_FOLDER_PATH = ""; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | PUBLIC_HEADERS_FOLDER_PATH = ""; 334 | SDKROOT = iphoneos; 335 | SKIP_INSTALL = YES; 336 | }; 337 | name = Debug; 338 | }; 339 | 149991F45E8A5B071ED66AB1CFF46E95 /* Release */ = { 340 | isa = XCBuildConfiguration; 341 | baseConfigurationReference = FD9BF65DAFDCDEA223E495F668BA590E /* Pods-WebSocketDemo.release.xcconfig */; 342 | buildSettings = { 343 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 344 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 345 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 346 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 350 | MACH_O_TYPE = staticlib; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | OTHER_LDFLAGS = ""; 353 | OTHER_LIBTOOLFLAGS = ""; 354 | PODS_ROOT = "$(SRCROOT)"; 355 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | SDKROOT = iphoneos; 358 | SKIP_INSTALL = YES; 359 | }; 360 | name = Release; 361 | }; 362 | 44CDBB6D11DE06DB64D6268622BDC47E /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_ANALYZER_NONNULL = YES; 367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 368 | CLANG_CXX_LIBRARY = "libc++"; 369 | CLANG_ENABLE_MODULES = YES; 370 | CLANG_ENABLE_OBJC_ARC = YES; 371 | CLANG_WARN_BOOL_CONVERSION = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 374 | CLANG_WARN_EMPTY_BODY = YES; 375 | CLANG_WARN_ENUM_CONVERSION = YES; 376 | CLANG_WARN_INT_CONVERSION = YES; 377 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 378 | CLANG_WARN_UNREACHABLE_CODE = YES; 379 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 380 | CODE_SIGNING_REQUIRED = NO; 381 | COPY_PHASE_STRIP = YES; 382 | ENABLE_NS_ASSERTIONS = NO; 383 | GCC_C_LANGUAGE_STANDARD = gnu99; 384 | GCC_PREPROCESSOR_DEFINITIONS = ( 385 | "POD_CONFIGURATION_RELEASE=1", 386 | "$(inherited)", 387 | ); 388 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 389 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 390 | GCC_WARN_UNDECLARED_SELECTOR = YES; 391 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 392 | GCC_WARN_UNUSED_FUNCTION = YES; 393 | GCC_WARN_UNUSED_VARIABLE = YES; 394 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 395 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 396 | STRIP_INSTALLED_PRODUCT = NO; 397 | SYMROOT = "${SRCROOT}/../build"; 398 | VALIDATE_PRODUCT = YES; 399 | }; 400 | name = Release; 401 | }; 402 | 5ACCCE9B77B8AF4F6956D0EB92DC449C /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 405 | buildSettings = { 406 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 408 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 409 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 410 | ENABLE_STRICT_OBJC_MSGSEND = YES; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 413 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 414 | MTL_ENABLE_DEBUG_INFO = NO; 415 | OTHER_LDFLAGS = ""; 416 | OTHER_LIBTOOLFLAGS = ""; 417 | PRIVATE_HEADERS_FOLDER_PATH = ""; 418 | PRODUCT_NAME = "$(TARGET_NAME)"; 419 | PUBLIC_HEADERS_FOLDER_PATH = ""; 420 | SDKROOT = iphoneos; 421 | SKIP_INSTALL = YES; 422 | }; 423 | name = Release; 424 | }; 425 | FC3DF12281ADB0B46F131F79CC4A04CD /* Debug */ = { 426 | isa = XCBuildConfiguration; 427 | baseConfigurationReference = 0A0AD8922F95636F3B334AAD5933FF29 /* Pods-WebSocketDemo.debug.xcconfig */; 428 | buildSettings = { 429 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 430 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 431 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 432 | DEBUG_INFORMATION_FORMAT = dwarf; 433 | ENABLE_STRICT_OBJC_MSGSEND = YES; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 436 | MACH_O_TYPE = staticlib; 437 | MTL_ENABLE_DEBUG_INFO = YES; 438 | OTHER_LDFLAGS = ""; 439 | OTHER_LIBTOOLFLAGS = ""; 440 | PODS_ROOT = "$(SRCROOT)"; 441 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | SDKROOT = iphoneos; 444 | SKIP_INSTALL = YES; 445 | }; 446 | name = Debug; 447 | }; 448 | /* End XCBuildConfiguration section */ 449 | 450 | /* Begin XCConfigurationList section */ 451 | 0C7560BE69FF7D88E973153B457CC00B /* Build configuration list for PBXNativeTarget "Pods-WebSocketDemo" */ = { 452 | isa = XCConfigurationList; 453 | buildConfigurations = ( 454 | FC3DF12281ADB0B46F131F79CC4A04CD /* Debug */, 455 | 149991F45E8A5B071ED66AB1CFF46E95 /* Release */, 456 | ); 457 | defaultConfigurationIsVisible = 0; 458 | defaultConfigurationName = Release; 459 | }; 460 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | 015A368F878AC3E2CEAE21DDE8026304 /* Debug */, 464 | 44CDBB6D11DE06DB64D6268622BDC47E /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | 8A05E5D1AB00659BE3ED9CA06F25AE77 /* Build configuration list for PBXNativeTarget "SocketRocket" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | 0B9BBDCE386CC40DB20E94BF4F654AC4 /* Debug */, 473 | 5ACCCE9B77B8AF4F6956D0EB92DC449C /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | /* End XCConfigurationList section */ 479 | }; 480 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 481 | } 482 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Pods.xcodeproj/xcuserdata/sunjun.xcuserdatad/xcschemes/Pods-WebSocketDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Pods.xcodeproj/xcuserdata/sunjun.xcuserdatad/xcschemes/SocketRocket.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Pods.xcodeproj/xcuserdata/sunjun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-WebSocketDemo.xcscheme 8 | 9 | isShown 10 | 11 | 12 | SocketRocket.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 0C8D56DF53D25726A27E225824845096 21 | 22 | primary 23 | 24 | 25 | 1B3FDED83149F7C943520562CB499E0D 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/SocketRocket/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2012 Square Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/SocketRocket/README.rst: -------------------------------------------------------------------------------- 1 | SocketRocket Objective-C WebSocket Client (beta) 2 | ================================================ 3 | A conforming WebSocket (`RFC 6455 `_) 4 | client library. 5 | 6 | `Test results for SocketRocket here `_. 7 | You can compare to what `modern browsers look like here 8 | `_. 9 | 10 | SocketRocket currently conforms to all ~300 of `Autobahn 11 | `_'s fuzzing tests (aside from 12 | two UTF-8 ones where it is merely *non-strict*. tests 6.4.2 and 6.4.4) 13 | 14 | Features/Design 15 | --------------- 16 | - TLS (wss) support. It uses CFStream so we get this for *free* 17 | - Uses NSStream/CFNetworking. Earlier implementations used ``dispatch_io``, 18 | however, this proved to be make TLS nearly impossible. Also I wanted this to 19 | work in iOS 4.x. (SocketRocket only supports 5.0 and above now) 20 | - Uses ARC. It uses the 4.0 compatible subset (no weak refs). 21 | - Seems to perform quite well 22 | - Parallel architecture. Most of the work is done in background worker queues. 23 | - Delegate-based. Had older versions that could use blocks too, but I felt it 24 | didn't blend well with retain cycles and just objective C in general. 25 | 26 | Changes 27 | ------- 28 | 29 | v0.3.1-beta2 - 2013-01-12 30 | ````````````````````````` 31 | 32 | - Stability fix for ``closeWithCode:reason:`` (Thanks @michaelpetrov!) 33 | - Actually clean up the NSStreams and remove them from their runloops 34 | - ``_SRRunLoopThread``'s ``main`` wasn't correctly wrapped with 35 | ``@autoreleasepool`` 36 | 37 | v0.3.1-beta1 - 2013-01-12 38 | ````````````````````````` 39 | 40 | - Cleaned up GCD so OS_OBJECT_USE_OBJC_RETAIN_RELEASE is optional 41 | - Removed deprecated ``dispatch_get_current_queue`` in favor of ``dispatch_queue_set_specific`` and ``dispatch_get_specific`` 42 | - Dropping support for iOS 4.0 (it may still work) 43 | 44 | 45 | Installing (iOS) 46 | ---------------- 47 | There's a few options. Choose one, or just figure it out 48 | 49 | - You can copy all the files in the SocketRocket group into your app. 50 | - Include SocketRocket as a subproject and use libSocketRocket 51 | 52 | If you do this, you must add -ObjC to your "other linker flags" option 53 | 54 | - For OS X you will have to repackage make a .framework target. I will take 55 | contributions. Message me if you are interested. 56 | 57 | 58 | Depending on how you configure your project you may need to ``#import`` either 59 | ```` or ``"SRWebSocket.h"`` 60 | 61 | Framework Dependencies 62 | `````````````````````` 63 | Your .app must be linked against the following frameworks/dylibs 64 | 65 | - libicucore.dylib 66 | - CFNetwork.framework 67 | - Security.framework 68 | - Foundation.framework 69 | 70 | Installing (OS X) 71 | ----------------- 72 | SocketRocket now has (64-bit only) OS X support. ``SocketRocket.framework`` 73 | inside Xcode project is for OS X only. It should be identical in function aside 74 | from the unicode validation. ICU isn't shipped with OS X which is what the 75 | original implementation used for unicode validation. The workaround is much 76 | more rudimentary and less robust. 77 | 78 | 1. Add SocketRocket.xcodeproj as either a subproject of your app or in your workspace. 79 | 2. Add ``SocketRocket.framework`` to the link libraries 80 | 3. If you don't have a "copy files" step for ``Framework``, create one 81 | 4. Add ``SocketRocket.framework`` to the "copy files" step. 82 | 83 | API 84 | --- 85 | The classes 86 | 87 | ``SRWebSocket`` 88 | ``````````````` 89 | The Web Socket. 90 | 91 | .. note:: ``SRWebSocket`` will retain itself between ``-(void)open`` and when it 92 | closes, errors, or fails. This is similar to how ``NSURLConnection`` behaves. 93 | (unlike ``NSURLConnection``, ``SRWebSocket`` won't retain the delegate) 94 | 95 | What you need to know 96 | 97 | .. code-block:: objective-c 98 | 99 | @interface SRWebSocket : NSObject 100 | 101 | // Make it with this 102 | - (id)initWithURLRequest:(NSURLRequest *)request; 103 | 104 | // Set this before opening 105 | @property (nonatomic, assign) id delegate; 106 | 107 | - (void)open; 108 | 109 | // Close it with this 110 | - (void)close; 111 | 112 | // Send a UTF8 String or Data 113 | - (void)send:(id)data; 114 | 115 | @end 116 | 117 | ``SRWebSocketDelegate`` 118 | ``````````````````````` 119 | You implement this 120 | 121 | .. code-block:: objective-c 122 | 123 | @protocol SRWebSocketDelegate 124 | 125 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 126 | 127 | @optional 128 | 129 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 130 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 131 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 132 | 133 | @end 134 | 135 | Known Issues/Server Todo's 136 | -------------------------- 137 | - Needs auth delegates (like in NSURLConnection) 138 | - Move the streams off the main runloop (most of the work is backgrounded uses 139 | GCD, but I just haven't gotten around to moving it off the main loop since I 140 | converted it from dispatch_io) 141 | - Re-implement server. I removed an existing implementation as well because it 142 | wasn't being used and I wasn't super happy with the interface. Will revisit 143 | this. 144 | - Separate framer and client logic. This will make it nicer when having a 145 | server. 146 | 147 | Testing 148 | ------- 149 | Included are setup scripts for the python testing environment. It comes 150 | packaged with vitualenv so all the dependencies are installed in userland. 151 | 152 | To run the short test from the command line, run:: 153 | 154 | make test 155 | 156 | To run all the tests, run:: 157 | 158 | make test_all 159 | 160 | The short tests don't include the performance tests. (the test harness is 161 | actually the bottleneck, not SocketRocket). 162 | 163 | The first time this is run, it may take a while to install the dependencies. It 164 | will be smooth sailing after that. After the test runs the makefile will open 165 | the results page in your browser. If nothing comes up, you failed. Working on 166 | making this interface a bit nicer. 167 | 168 | To run from the app, choose the ``SocketRocket`` target and run the test action 169 | (``cmd+u``). It runs the same thing, but makes it easier to debug. There is 170 | some serious pre/post hooks in the Test action. You can edit it to customize 171 | behavior. 172 | 173 | .. note:: Xcode only up to version 4.4 is currently supported for the test 174 | harness 175 | 176 | TestChat Demo Application 177 | ------------------------- 178 | SocketRocket includes a demo app, TestChat. It will "chat" with a listening 179 | websocket on port 9900. 180 | 181 | It's a simple project. Uses storyboard. Storyboard is sweet. 182 | 183 | 184 | TestChat Server 185 | ``````````````` 186 | We've included a small server for the chat app. It has a simple function. 187 | It will take a message and broadcast it to all other connected clients. 188 | 189 | We have to get some dependencies. We also want to reuse the virtualenv we made 190 | when we ran the tests. If you haven't run the tests yet, go into the 191 | SocketRocket root directory and type:: 192 | 193 | make test 194 | 195 | This will set up your `virtualenv `_. 196 | Now, in your terminal:: 197 | 198 | source .env/bin/activate 199 | pip install git+https://github.com/tornadoweb/tornado.git 200 | 201 | In the same terminal session, start the chatroom server:: 202 | 203 | python TestChatServer/py/chatroom.py 204 | 205 | There's also a Go implementation (with the latest weekly) where you can:: 206 | 207 | cd TestChatServer/go 208 | go run chatroom.go 209 | 210 | Chatting 211 | ```````` 212 | Now, start TestChat.app (just run the target in the Xcode project). If you had 213 | it started already you can hit the refresh button to reconnect. It should say 214 | "Connected!" on top. 215 | 216 | To talk with the app, open up your browser to `http://localhost:9000 `_ and 217 | start chatting. 218 | 219 | 220 | WebSocket Server Implementation Recommendations 221 | ----------------------------------------------- 222 | SocketRocket has been used with the following libraries: 223 | 224 | - `Tornado `_ 225 | - Go's `WebSocket package `_ or Gorilla's `version `_ 226 | - `Autobahn `_ (using its fuzzing 227 | client) 228 | 229 | The Tornado one is dirt simple and works like a charm. (`IPython notebook 230 | `_ uses it 231 | too). It's much easier to configure handlers and routes than in 232 | Autobahn/twisted. 233 | 234 | As far as Go's goes, it works in my limited testing. I much prefer go's 235 | concurrency model as well. Try it! You may like it. 236 | It could use some more control over things such as pings, etc., but I 237 | am sure it will come in time. 238 | 239 | Autobahn is a great test suite. The Python server code is good, and conforms 240 | well (obviously). However for me, twisted would be a deal-breaker for writing 241 | something new. I find it a bit too complex and heavy for a simple service. If 242 | you are already using twisted though, Autobahn is probably for you. 243 | 244 | Contributing 245 | ------------ 246 | We’re glad you’re interested in SocketRocket, and we’d love to see where you take it. Please read our `contributing guidelines `_ prior to submitting a Pull Request. -------------------------------------------------------------------------------- /WebSocketDemo/Pods/SocketRocket/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | #import 19 | 20 | typedef NS_ENUM(NSInteger, SRReadyState) { 21 | SR_CONNECTING = 0, 22 | SR_OPEN = 1, 23 | SR_CLOSING = 2, 24 | SR_CLOSED = 3, 25 | }; 26 | 27 | typedef enum SRStatusCode : NSInteger { 28 | // 0–999: Reserved and not used. 29 | SRStatusCodeNormal = 1000, 30 | SRStatusCodeGoingAway = 1001, 31 | SRStatusCodeProtocolError = 1002, 32 | SRStatusCodeUnhandledType = 1003, 33 | // 1004 reserved. 34 | SRStatusNoStatusReceived = 1005, 35 | SRStatusCodeAbnormal = 1006, 36 | SRStatusCodeInvalidUTF8 = 1007, 37 | SRStatusCodePolicyViolated = 1008, 38 | SRStatusCodeMessageTooBig = 1009, 39 | SRStatusCodeMissingExtension = 1010, 40 | SRStatusCodeInternalError = 1011, 41 | SRStatusCodeServiceRestart = 1012, 42 | SRStatusCodeTryAgainLater = 1013, 43 | // 1014: Reserved for future use by the WebSocket standard. 44 | SRStatusCodeTLSHandshake = 1015, 45 | // 1016–1999: Reserved for future use by the WebSocket standard. 46 | // 2000–2999: Reserved for use by WebSocket extensions. 47 | // 3000–3999: Available for use by libraries and frameworks. May not be used by applications. Available for registration at the IANA via first-come, first-serve. 48 | // 4000–4999: Available for use by applications. 49 | } SRStatusCode; 50 | 51 | @class SRWebSocket; 52 | 53 | extern NSString *const SRWebSocketErrorDomain; 54 | extern NSString *const SRHTTPResponseErrorKey; 55 | 56 | #pragma mark - SRWebSocketDelegate 57 | 58 | @protocol SRWebSocketDelegate; 59 | 60 | #pragma mark - SRWebSocket 61 | 62 | @interface SRWebSocket : NSObject 63 | 64 | @property (nonatomic, weak) id delegate; 65 | 66 | @property (nonatomic, readonly) SRReadyState readyState; 67 | @property (nonatomic, readonly, retain) NSURL *url; 68 | 69 | 70 | @property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders; 71 | 72 | // Optional array of cookies (NSHTTPCookie objects) to apply to the connections 73 | @property (nonatomic, readwrite) NSArray * requestCookies; 74 | 75 | // This returns the negotiated protocol. 76 | // It will be nil until after the handshake completes. 77 | @property (nonatomic, readonly, copy) NSString *protocol; 78 | 79 | // Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. 80 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 81 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 82 | - (id)initWithURLRequest:(NSURLRequest *)request; 83 | 84 | // Some helper constructors. 85 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 86 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 87 | - (id)initWithURL:(NSURL *)url; 88 | 89 | // Delegate queue will be dispatch_main_queue by default. 90 | // You cannot set both OperationQueue and dispatch_queue. 91 | - (void)setDelegateOperationQueue:(NSOperationQueue*) queue; 92 | - (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; 93 | 94 | // By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. 95 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 96 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 97 | 98 | // SRWebSockets are intended for one-time-use only. Open should be called once and only once. 99 | - (void)open; 100 | 101 | - (void)close; 102 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 103 | 104 | // Send a UTF8 String or Data. 105 | - (void)send:(id)data; 106 | 107 | // Send Data (can be nil) in a ping message. 108 | - (void)sendPing:(NSData *)data; 109 | 110 | @end 111 | 112 | #pragma mark - SRWebSocketDelegate 113 | 114 | @protocol SRWebSocketDelegate 115 | 116 | // message will either be an NSString if the server is using text 117 | // or NSData if the server is using binary. 118 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 119 | 120 | @optional 121 | 122 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 123 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 124 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 125 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; 126 | 127 | // Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES. 128 | - (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket; 129 | 130 | @end 131 | 132 | #pragma mark - NSURLRequest (SRCertificateAdditions) 133 | 134 | @interface NSURLRequest (SRCertificateAdditions) 135 | 136 | @property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; 137 | 138 | @end 139 | 140 | #pragma mark - NSMutableURLRequest (SRCertificateAdditions) 141 | 142 | @interface NSMutableURLRequest (SRCertificateAdditions) 143 | 144 | @property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; 145 | 146 | @end 147 | 148 | #pragma mark - NSRunLoop (SRWebSocket) 149 | 150 | @interface NSRunLoop (SRWebSocket) 151 | 152 | + (NSRunLoop *)SR_networkRunLoop; 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/SocketRocket/SocketRocket/SRWebSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | #import "SRWebSocket.h" 19 | 20 | #if TARGET_OS_IPHONE 21 | #define HAS_ICU 22 | #endif 23 | 24 | #ifdef HAS_ICU 25 | #import 26 | #endif 27 | 28 | #if TARGET_OS_IPHONE 29 | #import 30 | #else 31 | #import 32 | #endif 33 | 34 | #import 35 | #import 36 | 37 | #if OS_OBJECT_USE_OBJC_RETAIN_RELEASE 38 | #define sr_dispatch_retain(x) 39 | #define sr_dispatch_release(x) 40 | #define maybe_bridge(x) ((__bridge void *) x) 41 | #else 42 | #define sr_dispatch_retain(x) dispatch_retain(x) 43 | #define sr_dispatch_release(x) dispatch_release(x) 44 | #define maybe_bridge(x) (x) 45 | #endif 46 | 47 | #if !__has_feature(objc_arc) 48 | #error SocketRocket must be compiled with ARC enabled 49 | #endif 50 | 51 | 52 | typedef enum { 53 | SROpCodeTextFrame = 0x1, 54 | SROpCodeBinaryFrame = 0x2, 55 | // 3-7 reserved. 56 | SROpCodeConnectionClose = 0x8, 57 | SROpCodePing = 0x9, 58 | SROpCodePong = 0xA, 59 | // B-F reserved. 60 | } SROpCode; 61 | 62 | typedef struct { 63 | BOOL fin; 64 | // BOOL rsv1; 65 | // BOOL rsv2; 66 | // BOOL rsv3; 67 | uint8_t opcode; 68 | BOOL masked; 69 | uint64_t payload_length; 70 | } frame_header; 71 | 72 | static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 73 | 74 | static inline int32_t validate_dispatch_data_partial_string(NSData *data); 75 | static inline void SRFastLog(NSString *format, ...); 76 | 77 | @interface NSData (SRWebSocket) 78 | 79 | - (NSString *)stringBySHA1ThenBase64Encoding; 80 | 81 | @end 82 | 83 | 84 | @interface NSString (SRWebSocket) 85 | 86 | - (NSString *)stringBySHA1ThenBase64Encoding; 87 | 88 | @end 89 | 90 | 91 | @interface NSURL (SRWebSocket) 92 | 93 | // The origin isn't really applicable for a native application. 94 | // So instead, just map ws -> http and wss -> https. 95 | - (NSString *)SR_origin; 96 | 97 | @end 98 | 99 | 100 | @interface _SRRunLoopThread : NSThread 101 | 102 | @property (nonatomic, readonly) NSRunLoop *runLoop; 103 | 104 | @end 105 | 106 | 107 | static NSString *newSHA1String(const char *bytes, size_t length) { 108 | uint8_t md[CC_SHA1_DIGEST_LENGTH]; 109 | 110 | assert(length >= 0); 111 | assert(length <= UINT32_MAX); 112 | CC_SHA1(bytes, (CC_LONG)length, md); 113 | 114 | NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; 115 | 116 | if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 117 | return [data base64EncodedStringWithOptions:0]; 118 | } 119 | 120 | #pragma clang diagnostic push 121 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 122 | return [data base64Encoding]; 123 | #pragma clang diagnostic pop 124 | } 125 | 126 | @implementation NSData (SRWebSocket) 127 | 128 | - (NSString *)stringBySHA1ThenBase64Encoding; 129 | { 130 | return newSHA1String(self.bytes, self.length); 131 | } 132 | 133 | @end 134 | 135 | 136 | @implementation NSString (SRWebSocket) 137 | 138 | - (NSString *)stringBySHA1ThenBase64Encoding; 139 | { 140 | return newSHA1String(self.UTF8String, self.length); 141 | } 142 | 143 | @end 144 | 145 | NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; 146 | NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode"; 147 | 148 | // Returns number of bytes consumed. Returning 0 means you didn't match. 149 | // Sends bytes to callback handler; 150 | typedef size_t (^stream_scanner)(NSData *collected_data); 151 | 152 | typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); 153 | 154 | @interface SRIOConsumer : NSObject { 155 | stream_scanner _scanner; 156 | data_callback _handler; 157 | size_t _bytesNeeded; 158 | BOOL _readToCurrentFrame; 159 | BOOL _unmaskBytes; 160 | } 161 | @property (nonatomic, copy, readonly) stream_scanner consumer; 162 | @property (nonatomic, copy, readonly) data_callback handler; 163 | @property (nonatomic, assign) size_t bytesNeeded; 164 | @property (nonatomic, assign, readonly) BOOL readToCurrentFrame; 165 | @property (nonatomic, assign, readonly) BOOL unmaskBytes; 166 | 167 | @end 168 | 169 | // This class is not thread-safe, and is expected to always be run on the same queue. 170 | @interface SRIOConsumerPool : NSObject 171 | 172 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 173 | 174 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 175 | - (void)returnConsumer:(SRIOConsumer *)consumer; 176 | 177 | @end 178 | 179 | @interface SRWebSocket () 180 | 181 | @property (nonatomic) SRReadyState readyState; 182 | 183 | @property (nonatomic) NSOperationQueue *delegateOperationQueue; 184 | @property (nonatomic) dispatch_queue_t delegateDispatchQueue; 185 | 186 | // Specifies whether SSL trust chain should NOT be evaluated. 187 | // By default this flag is set to NO, meaning only secure SSL connections are allowed. 188 | // For DEBUG builds this flag is ignored, and SSL connections are allowed regardless 189 | // of the certificate trust configuration 190 | @property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates; 191 | 192 | @end 193 | 194 | 195 | @implementation SRWebSocket { 196 | NSInteger _webSocketVersion; 197 | 198 | NSOperationQueue *_delegateOperationQueue; 199 | dispatch_queue_t _delegateDispatchQueue; 200 | 201 | dispatch_queue_t _workQueue; 202 | NSMutableArray *_consumers; 203 | 204 | NSInputStream *_inputStream; 205 | NSOutputStream *_outputStream; 206 | 207 | NSMutableData *_readBuffer; 208 | NSUInteger _readBufferOffset; 209 | 210 | NSMutableData *_outputBuffer; 211 | NSUInteger _outputBufferOffset; 212 | 213 | uint8_t _currentFrameOpcode; 214 | size_t _currentFrameCount; 215 | size_t _readOpCount; 216 | uint32_t _currentStringScanPosition; 217 | NSMutableData *_currentFrameData; 218 | 219 | NSString *_closeReason; 220 | 221 | NSString *_secKey; 222 | NSString *_basicAuthorizationString; 223 | 224 | BOOL _pinnedCertFound; 225 | 226 | uint8_t _currentReadMaskKey[4]; 227 | size_t _currentReadMaskOffset; 228 | 229 | BOOL _consumerStopped; 230 | 231 | BOOL _closeWhenFinishedWriting; 232 | BOOL _failed; 233 | 234 | BOOL _secure; 235 | NSURLRequest *_urlRequest; 236 | 237 | BOOL _sentClose; 238 | BOOL _didFail; 239 | BOOL _cleanupScheduled; 240 | int _closeCode; 241 | 242 | BOOL _isPumping; 243 | 244 | NSMutableSet *_scheduledRunloops; 245 | 246 | // We use this to retain ourselves. 247 | __strong SRWebSocket *_selfRetain; 248 | 249 | NSArray *_requestedProtocols; 250 | SRIOConsumerPool *_consumerPool; 251 | } 252 | 253 | @synthesize delegate = _delegate; 254 | @synthesize url = _url; 255 | @synthesize readyState = _readyState; 256 | @synthesize protocol = _protocol; 257 | 258 | static __strong NSData *CRLFCRLF; 259 | 260 | + (void)initialize; 261 | { 262 | CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 263 | } 264 | 265 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 266 | { 267 | self = [super init]; 268 | if (self) { 269 | assert(request.URL); 270 | _url = request.URL; 271 | _urlRequest = request; 272 | _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates; 273 | 274 | _requestedProtocols = [protocols copy]; 275 | 276 | [self _SR_commonInit]; 277 | } 278 | 279 | return self; 280 | } 281 | 282 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 283 | { 284 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO]; 285 | } 286 | 287 | - (id)initWithURLRequest:(NSURLRequest *)request; 288 | { 289 | return [self initWithURLRequest:request protocols:nil]; 290 | } 291 | 292 | - (id)initWithURL:(NSURL *)url; 293 | { 294 | return [self initWithURL:url protocols:nil]; 295 | } 296 | 297 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 298 | { 299 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 300 | return [self initWithURLRequest:request protocols:protocols]; 301 | } 302 | 303 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 304 | { 305 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 306 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates]; 307 | } 308 | 309 | - (void)_SR_commonInit; 310 | { 311 | NSString *scheme = _url.scheme.lowercaseString; 312 | assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); 313 | 314 | if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { 315 | _secure = YES; 316 | } 317 | 318 | _readyState = SR_CONNECTING; 319 | _consumerStopped = YES; 320 | _webSocketVersion = 13; 321 | 322 | _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 323 | 324 | // Going to set a specific on the queue so we can validate we're on the work queue 325 | dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); 326 | 327 | _delegateDispatchQueue = dispatch_get_main_queue(); 328 | sr_dispatch_retain(_delegateDispatchQueue); 329 | 330 | _readBuffer = [[NSMutableData alloc] init]; 331 | _outputBuffer = [[NSMutableData alloc] init]; 332 | 333 | _currentFrameData = [[NSMutableData alloc] init]; 334 | 335 | _consumers = [[NSMutableArray alloc] init]; 336 | 337 | _consumerPool = [[SRIOConsumerPool alloc] init]; 338 | 339 | _scheduledRunloops = [[NSMutableSet alloc] init]; 340 | 341 | [self _initializeStreams]; 342 | 343 | // default handlers 344 | } 345 | 346 | - (void)assertOnWorkQueue; 347 | { 348 | assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); 349 | } 350 | 351 | - (void)dealloc 352 | { 353 | _inputStream.delegate = nil; 354 | _outputStream.delegate = nil; 355 | 356 | [_inputStream close]; 357 | [_outputStream close]; 358 | 359 | if (_workQueue) { 360 | sr_dispatch_release(_workQueue); 361 | _workQueue = NULL; 362 | } 363 | 364 | if (_receivedHTTPHeaders) { 365 | CFRelease(_receivedHTTPHeaders); 366 | _receivedHTTPHeaders = NULL; 367 | } 368 | 369 | if (_delegateDispatchQueue) { 370 | sr_dispatch_release(_delegateDispatchQueue); 371 | _delegateDispatchQueue = NULL; 372 | } 373 | } 374 | 375 | #ifndef NDEBUG 376 | 377 | - (void)setReadyState:(SRReadyState)aReadyState; 378 | { 379 | assert(aReadyState > _readyState); 380 | _readyState = aReadyState; 381 | } 382 | 383 | #endif 384 | 385 | - (void)open; 386 | { 387 | assert(_url); 388 | NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once"); 389 | 390 | _selfRetain = self; 391 | 392 | if (_urlRequest.timeoutInterval > 0) 393 | { 394 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC); 395 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 396 | if (self.readyState == SR_CONNECTING) 397 | [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]]; 398 | }); 399 | } 400 | 401 | [self openConnection]; 402 | } 403 | 404 | // Calls block on delegate queue 405 | - (void)_performDelegateBlock:(dispatch_block_t)block; 406 | { 407 | if (_delegateOperationQueue) { 408 | [_delegateOperationQueue addOperationWithBlock:block]; 409 | } else { 410 | assert(_delegateDispatchQueue); 411 | dispatch_async(_delegateDispatchQueue, block); 412 | } 413 | } 414 | 415 | - (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; 416 | { 417 | if (queue) { 418 | sr_dispatch_retain(queue); 419 | } 420 | 421 | if (_delegateDispatchQueue) { 422 | sr_dispatch_release(_delegateDispatchQueue); 423 | } 424 | 425 | _delegateDispatchQueue = queue; 426 | } 427 | 428 | - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; 429 | { 430 | NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); 431 | 432 | if (acceptHeader == nil) { 433 | return NO; 434 | } 435 | 436 | NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; 437 | NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; 438 | 439 | return [acceptHeader isEqualToString:expectedAccept]; 440 | } 441 | 442 | - (void)_HTTPHeadersDidFinish; 443 | { 444 | NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); 445 | 446 | if (responseCode >= 400) { 447 | SRFastLog(@"Request failed with response code %d", responseCode); 448 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]]; 449 | return; 450 | } 451 | 452 | if(![self _checkHandshake:_receivedHTTPHeaders]) { 453 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; 454 | return; 455 | } 456 | 457 | NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); 458 | if (negotiatedProtocol) { 459 | // Make sure we requested the protocol 460 | if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { 461 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; 462 | return; 463 | } 464 | 465 | _protocol = negotiatedProtocol; 466 | } 467 | 468 | self.readyState = SR_OPEN; 469 | 470 | if (!_didFail) { 471 | [self _readFrameNew]; 472 | } 473 | 474 | [self _performDelegateBlock:^{ 475 | if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { 476 | [self.delegate webSocketDidOpen:self]; 477 | }; 478 | }]; 479 | } 480 | 481 | 482 | - (void)_readHTTPHeader; 483 | { 484 | if (_receivedHTTPHeaders == NULL) { 485 | _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); 486 | } 487 | 488 | [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { 489 | CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); 490 | 491 | if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { 492 | SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); 493 | [self _HTTPHeadersDidFinish]; 494 | } else { 495 | [self _readHTTPHeader]; 496 | } 497 | }]; 498 | } 499 | 500 | - (void)didConnect; 501 | { 502 | SRFastLog(@"Connected"); 503 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); 504 | 505 | // Set host first so it defaults 506 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); 507 | 508 | NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; 509 | SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); 510 | 511 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 512 | _secKey = [keyBytes base64EncodedStringWithOptions:0]; 513 | } else { 514 | #pragma clang diagnostic push 515 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 516 | _secKey = [keyBytes base64Encoding]; 517 | #pragma clang diagnostic pop 518 | } 519 | 520 | assert([_secKey length] == 24); 521 | 522 | // Apply cookies if any have been provided 523 | NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]]; 524 | for (NSString * cookieKey in cookies) { 525 | NSString * cookieValue = [cookies objectForKey:cookieKey]; 526 | if ([cookieKey length] && [cookieValue length]) { 527 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue); 528 | } 529 | } 530 | 531 | // set header for http basic auth 532 | if (_url.user.length && _url.password.length) { 533 | NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding]; 534 | NSString *userAndPasswordBase64Encoded; 535 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 536 | userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0]; 537 | } else { 538 | #pragma clang diagnostic push 539 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 540 | userAndPasswordBase64Encoded = [userAndPassword base64Encoding]; 541 | #pragma clang diagnostic pop 542 | } 543 | _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded]; 544 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString); 545 | } 546 | 547 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); 548 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); 549 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); 550 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); 551 | 552 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); 553 | 554 | if (_requestedProtocols) { 555 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); 556 | } 557 | 558 | [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 559 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); 560 | }]; 561 | 562 | NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); 563 | 564 | CFRelease(request); 565 | 566 | [self _writeData:message]; 567 | [self _readHTTPHeader]; 568 | } 569 | 570 | - (void)_initializeStreams; 571 | { 572 | assert(_url.port.unsignedIntValue <= UINT32_MAX); 573 | uint32_t port = _url.port.unsignedIntValue; 574 | if (port == 0) { 575 | if (!_secure) { 576 | port = 80; 577 | } else { 578 | port = 443; 579 | } 580 | } 581 | NSString *host = _url.host; 582 | 583 | CFReadStreamRef readStream = NULL; 584 | CFWriteStreamRef writeStream = NULL; 585 | 586 | CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); 587 | 588 | _outputStream = CFBridgingRelease(writeStream); 589 | _inputStream = CFBridgingRelease(readStream); 590 | 591 | _inputStream.delegate = self; 592 | _outputStream.delegate = self; 593 | } 594 | 595 | - (void)_updateSecureStreamOptions; 596 | { 597 | if (_secure) { 598 | NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; 599 | 600 | [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; 601 | 602 | // If we're using pinned certs, don't validate the certificate chain 603 | if ([_urlRequest SR_SSLPinnedCertificates].count) { 604 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 605 | } 606 | 607 | #if DEBUG 608 | self.allowsUntrustedSSLCertificates = YES; 609 | #endif 610 | 611 | if (self.allowsUntrustedSSLCertificates) { 612 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 613 | SRFastLog(@"Allowing connection to any root cert"); 614 | } 615 | 616 | [_outputStream setProperty:SSLOptions 617 | forKey:(__bridge id)kCFStreamPropertySSLSettings]; 618 | } 619 | 620 | _inputStream.delegate = self; 621 | _outputStream.delegate = self; 622 | 623 | [self setupNetworkServiceType:_urlRequest.networkServiceType]; 624 | } 625 | 626 | - (void)setupNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType 627 | { 628 | NSString *networkServiceType; 629 | switch (requestNetworkServiceType) { 630 | case NSURLNetworkServiceTypeDefault: 631 | break; 632 | case NSURLNetworkServiceTypeVoIP: { 633 | networkServiceType = NSStreamNetworkServiceTypeVoIP; 634 | #if TARGET_OS_IPHONE && __IPHONE_9_0 635 | if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_3) { 636 | static dispatch_once_t predicate; 637 | dispatch_once(&predicate, ^{ 638 | NSLog(@"SocketRocket: %@ - this service type is deprecated in favor of using PushKit for VoIP control", networkServiceType); 639 | }); 640 | } 641 | #endif 642 | break; 643 | } 644 | case NSURLNetworkServiceTypeVideo: 645 | networkServiceType = NSStreamNetworkServiceTypeVideo; 646 | break; 647 | case NSURLNetworkServiceTypeBackground: 648 | networkServiceType = NSStreamNetworkServiceTypeBackground; 649 | break; 650 | case NSURLNetworkServiceTypeVoice: 651 | networkServiceType = NSStreamNetworkServiceTypeVoice; 652 | break; 653 | } 654 | 655 | if (networkServiceType != nil) { 656 | [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 657 | [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 658 | } 659 | } 660 | 661 | - (void)openConnection; 662 | { 663 | [self _updateSecureStreamOptions]; 664 | 665 | if (!_scheduledRunloops.count) { 666 | [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; 667 | } 668 | 669 | 670 | [_outputStream open]; 671 | [_inputStream open]; 672 | } 673 | 674 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 675 | { 676 | [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; 677 | [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; 678 | 679 | [_scheduledRunloops addObject:@[aRunLoop, mode]]; 680 | } 681 | 682 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 683 | { 684 | [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; 685 | [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; 686 | 687 | [_scheduledRunloops removeObject:@[aRunLoop, mode]]; 688 | } 689 | 690 | - (void)close; 691 | { 692 | [self closeWithCode:SRStatusCodeNormal reason:nil]; 693 | } 694 | 695 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 696 | { 697 | assert(code); 698 | dispatch_async(_workQueue, ^{ 699 | if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { 700 | return; 701 | } 702 | 703 | BOOL wasConnecting = self.readyState == SR_CONNECTING; 704 | 705 | self.readyState = SR_CLOSING; 706 | 707 | SRFastLog(@"Closing with code %d reason %@", code, reason); 708 | 709 | if (wasConnecting) { 710 | [self closeConnection]; 711 | return; 712 | } 713 | 714 | size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 715 | NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; 716 | NSData *payload = mutablePayload; 717 | 718 | ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); 719 | 720 | if (reason) { 721 | NSRange remainingRange = {0}; 722 | 723 | NSUInteger usedLength = 0; 724 | 725 | BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange]; 726 | #pragma unused (success) 727 | 728 | assert(success); 729 | assert(remainingRange.length == 0); 730 | 731 | if (usedLength != maxMsgSize) { 732 | payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; 733 | } 734 | } 735 | 736 | 737 | [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; 738 | }); 739 | } 740 | 741 | - (void)_closeWithProtocolError:(NSString *)message; 742 | { 743 | // Need to shunt this on the _callbackQueue first to see if they received any messages 744 | [self _performDelegateBlock:^{ 745 | [self closeWithCode:SRStatusCodeProtocolError reason:message]; 746 | dispatch_async(_workQueue, ^{ 747 | [self closeConnection]; 748 | }); 749 | }]; 750 | } 751 | 752 | - (void)_failWithError:(NSError *)error; 753 | { 754 | dispatch_async(_workQueue, ^{ 755 | if (self.readyState != SR_CLOSED) { 756 | _failed = YES; 757 | [self _performDelegateBlock:^{ 758 | if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { 759 | [self.delegate webSocket:self didFailWithError:error]; 760 | } 761 | }]; 762 | 763 | self.readyState = SR_CLOSED; 764 | 765 | SRFastLog(@"Failing with error %@", error.localizedDescription); 766 | 767 | [self closeConnection]; 768 | [self _scheduleCleanup]; 769 | } 770 | }); 771 | } 772 | 773 | - (void)_writeData:(NSData *)data; 774 | { 775 | [self assertOnWorkQueue]; 776 | 777 | if (_closeWhenFinishedWriting) { 778 | return; 779 | } 780 | [_outputBuffer appendData:data]; 781 | [self _pumpWriting]; 782 | } 783 | 784 | - (void)send:(id)data; 785 | { 786 | NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); 787 | // TODO: maybe not copy this for performance 788 | data = [data copy]; 789 | dispatch_async(_workQueue, ^{ 790 | if ([data isKindOfClass:[NSString class]]) { 791 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; 792 | } else if ([data isKindOfClass:[NSData class]]) { 793 | [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; 794 | } else if (data == nil) { 795 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; 796 | } else { 797 | assert(NO); 798 | } 799 | }); 800 | } 801 | 802 | - (void)sendPing:(NSData *)data; 803 | { 804 | NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open"); 805 | // TODO: maybe not copy this for performance 806 | data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty 807 | dispatch_async(_workQueue, ^{ 808 | [self _sendFrameWithOpcode:SROpCodePing data:data]; 809 | }); 810 | } 811 | 812 | - (void)handlePing:(NSData *)pingData; 813 | { 814 | // Need to pingpong this off _callbackQueue first to make sure messages happen in order 815 | [self _performDelegateBlock:^{ 816 | dispatch_async(_workQueue, ^{ 817 | [self _sendFrameWithOpcode:SROpCodePong data:pingData]; 818 | }); 819 | }]; 820 | } 821 | 822 | - (void)handlePong:(NSData *)pongData; 823 | { 824 | SRFastLog(@"Received pong"); 825 | [self _performDelegateBlock:^{ 826 | if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) { 827 | [self.delegate webSocket:self didReceivePong:pongData]; 828 | } 829 | }]; 830 | } 831 | 832 | - (void)_handleMessage:(id)message 833 | { 834 | SRFastLog(@"Received message"); 835 | [self _performDelegateBlock:^{ 836 | [self.delegate webSocket:self didReceiveMessage:message]; 837 | }]; 838 | } 839 | 840 | 841 | static inline BOOL closeCodeIsValid(int closeCode) { 842 | if (closeCode < 1000) { 843 | return NO; 844 | } 845 | 846 | if (closeCode >= 1000 && closeCode <= 1011) { 847 | if (closeCode == 1004 || 848 | closeCode == 1005 || 849 | closeCode == 1006) { 850 | return NO; 851 | } 852 | return YES; 853 | } 854 | 855 | if (closeCode >= 3000 && closeCode <= 3999) { 856 | return YES; 857 | } 858 | 859 | if (closeCode >= 4000 && closeCode <= 4999) { 860 | return YES; 861 | } 862 | 863 | return NO; 864 | } 865 | 866 | // Note from RFC: 867 | // 868 | // If there is a body, the first two 869 | // bytes of the body MUST be a 2-byte unsigned integer (in network byte 870 | // order) representing a status code with value /code/ defined in 871 | // Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 872 | // encoded data with value /reason/, the interpretation of which is not 873 | // defined by this specification. 874 | 875 | - (void)handleCloseWithData:(NSData *)data; 876 | { 877 | size_t dataSize = data.length; 878 | __block uint16_t closeCode = 0; 879 | 880 | SRFastLog(@"Received close frame"); 881 | 882 | if (dataSize == 1) { 883 | // TODO handle error 884 | [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; 885 | return; 886 | } else if (dataSize >= 2) { 887 | [data getBytes:&closeCode length:sizeof(closeCode)]; 888 | _closeCode = EndianU16_BtoN(closeCode); 889 | if (!closeCodeIsValid(_closeCode)) { 890 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; 891 | return; 892 | } 893 | if (dataSize > 2) { 894 | _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; 895 | if (!_closeReason) { 896 | [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; 897 | return; 898 | } 899 | } 900 | } else { 901 | _closeCode = SRStatusNoStatusReceived; 902 | } 903 | 904 | [self assertOnWorkQueue]; 905 | 906 | if (self.readyState == SR_OPEN) { 907 | [self closeWithCode:1000 reason:nil]; 908 | } 909 | dispatch_async(_workQueue, ^{ 910 | [self closeConnection]; 911 | }); 912 | } 913 | 914 | - (void)closeConnection; 915 | { 916 | [self assertOnWorkQueue]; 917 | SRFastLog(@"Trying to disconnect"); 918 | _closeWhenFinishedWriting = YES; 919 | [self _pumpWriting]; 920 | } 921 | 922 | - (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; 923 | { 924 | // Check that the current data is valid UTF8 925 | 926 | BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); 927 | if (!isControlFrame) { 928 | [self _readFrameNew]; 929 | } else { 930 | dispatch_async(_workQueue, ^{ 931 | [self _readFrameContinue]; 932 | }); 933 | } 934 | 935 | //frameData will be copied before passing to handlers 936 | //otherwise there can be misbehaviours when value at the pointer is changed 937 | switch (opcode) { 938 | case SROpCodeTextFrame: { 939 | if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] && ![self.delegate webSocketShouldConvertTextFrameToString:self]) { 940 | [self _handleMessage:[frameData copy]]; 941 | } else { 942 | NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; 943 | if (str == nil && frameData) { 944 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 945 | dispatch_async(_workQueue, ^{ 946 | [self closeConnection]; 947 | }); 948 | return; 949 | } 950 | [self _handleMessage:str]; 951 | } 952 | break; 953 | } 954 | case SROpCodeBinaryFrame: 955 | [self _handleMessage:[frameData copy]]; 956 | break; 957 | case SROpCodeConnectionClose: 958 | [self handleCloseWithData:[frameData copy]]; 959 | break; 960 | case SROpCodePing: 961 | [self handlePing:[frameData copy]]; 962 | break; 963 | case SROpCodePong: 964 | [self handlePong:[frameData copy]]; 965 | break; 966 | default: 967 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; 968 | // TODO: Handle invalid opcode 969 | break; 970 | } 971 | } 972 | 973 | - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; 974 | { 975 | assert(frame_header.opcode != 0); 976 | 977 | if (self.readyState == SR_CLOSED) { 978 | return; 979 | } 980 | 981 | 982 | BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); 983 | 984 | if (isControlFrame && !frame_header.fin) { 985 | [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; 986 | return; 987 | } 988 | 989 | if (isControlFrame && frame_header.payload_length >= 126) { 990 | [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; 991 | return; 992 | } 993 | 994 | if (!isControlFrame) { 995 | _currentFrameOpcode = frame_header.opcode; 996 | _currentFrameCount += 1; 997 | } 998 | 999 | if (frame_header.payload_length == 0) { 1000 | if (isControlFrame) { 1001 | [self _handleFrameWithData:curData opCode:frame_header.opcode]; 1002 | } else { 1003 | if (frame_header.fin) { 1004 | [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; 1005 | } else { 1006 | // TODO add assert that opcode is not a control; 1007 | [self _readFrameContinue]; 1008 | } 1009 | } 1010 | } else { 1011 | assert(frame_header.payload_length <= SIZE_T_MAX); 1012 | [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { 1013 | if (isControlFrame) { 1014 | [self _handleFrameWithData:newData opCode:frame_header.opcode]; 1015 | } else { 1016 | if (frame_header.fin) { 1017 | [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; 1018 | } else { 1019 | // TODO add assert that opcode is not a control; 1020 | [self _readFrameContinue]; 1021 | } 1022 | 1023 | } 1024 | } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; 1025 | } 1026 | } 1027 | 1028 | /* From RFC: 1029 | 1030 | 0 1 2 3 1031 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1032 | +-+-+-+-+-------+-+-------------+-------------------------------+ 1033 | |F|R|R|R| opcode|M| Payload len | Extended payload length | 1034 | |I|S|S|S| (4) |A| (7) | (16/64) | 1035 | |N|V|V|V| |S| | (if payload len==126/127) | 1036 | | |1|2|3| |K| | | 1037 | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 1038 | | Extended payload length continued, if payload len == 127 | 1039 | + - - - - - - - - - - - - - - - +-------------------------------+ 1040 | | |Masking-key, if MASK set to 1 | 1041 | +-------------------------------+-------------------------------+ 1042 | | Masking-key (continued) | Payload Data | 1043 | +-------------------------------- - - - - - - - - - - - - - - - + 1044 | : Payload Data continued ... : 1045 | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 1046 | | Payload Data continued ... | 1047 | +---------------------------------------------------------------+ 1048 | */ 1049 | 1050 | static const uint8_t SRFinMask = 0x80; 1051 | static const uint8_t SROpCodeMask = 0x0F; 1052 | static const uint8_t SRRsvMask = 0x70; 1053 | static const uint8_t SRMaskMask = 0x80; 1054 | static const uint8_t SRPayloadLenMask = 0x7F; 1055 | 1056 | 1057 | - (void)_readFrameContinue; 1058 | { 1059 | assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); 1060 | 1061 | [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { 1062 | __block frame_header header = {0}; 1063 | 1064 | const uint8_t *headerBuffer = data.bytes; 1065 | assert(data.length >= 2); 1066 | 1067 | if (headerBuffer[0] & SRRsvMask) { 1068 | [self _closeWithProtocolError:@"Server used RSV bits"]; 1069 | return; 1070 | } 1071 | 1072 | uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); 1073 | 1074 | BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); 1075 | 1076 | if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { 1077 | [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; 1078 | return; 1079 | } 1080 | 1081 | if (receivedOpcode == 0 && self->_currentFrameCount == 0) { 1082 | [self _closeWithProtocolError:@"cannot continue a message"]; 1083 | return; 1084 | } 1085 | 1086 | header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; 1087 | 1088 | header.fin = !!(SRFinMask & headerBuffer[0]); 1089 | 1090 | 1091 | header.masked = !!(SRMaskMask & headerBuffer[1]); 1092 | header.payload_length = SRPayloadLenMask & headerBuffer[1]; 1093 | 1094 | headerBuffer = NULL; 1095 | 1096 | if (header.masked) { 1097 | [self _closeWithProtocolError:@"Client must receive unmasked data"]; 1098 | } 1099 | 1100 | size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; 1101 | 1102 | if (header.payload_length == 126) { 1103 | extra_bytes_needed += sizeof(uint16_t); 1104 | } else if (header.payload_length == 127) { 1105 | extra_bytes_needed += sizeof(uint64_t); 1106 | } 1107 | 1108 | if (extra_bytes_needed == 0) { 1109 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1110 | } else { 1111 | [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { 1112 | size_t mapped_size = data.length; 1113 | #pragma unused (mapped_size) 1114 | const void *mapped_buffer = data.bytes; 1115 | size_t offset = 0; 1116 | 1117 | if (header.payload_length == 126) { 1118 | assert(mapped_size >= sizeof(uint16_t)); 1119 | uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); 1120 | header.payload_length = newLen; 1121 | offset += sizeof(uint16_t); 1122 | } else if (header.payload_length == 127) { 1123 | assert(mapped_size >= sizeof(uint64_t)); 1124 | header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); 1125 | offset += sizeof(uint64_t); 1126 | } else { 1127 | assert(header.payload_length < 126 && header.payload_length >= 0); 1128 | } 1129 | 1130 | if (header.masked) { 1131 | assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); 1132 | memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); 1133 | } 1134 | 1135 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1136 | } readToCurrentFrame:NO unmaskBytes:NO]; 1137 | } 1138 | } readToCurrentFrame:NO unmaskBytes:NO]; 1139 | } 1140 | 1141 | - (void)_readFrameNew; 1142 | { 1143 | dispatch_async(_workQueue, ^{ 1144 | [_currentFrameData setLength:0]; 1145 | 1146 | _currentFrameOpcode = 0; 1147 | _currentFrameCount = 0; 1148 | _readOpCount = 0; 1149 | _currentStringScanPosition = 0; 1150 | 1151 | [self _readFrameContinue]; 1152 | }); 1153 | } 1154 | 1155 | - (void)_pumpWriting; 1156 | { 1157 | [self assertOnWorkQueue]; 1158 | 1159 | NSUInteger dataLength = _outputBuffer.length; 1160 | if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { 1161 | NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; 1162 | if (bytesWritten == -1) { 1163 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; 1164 | return; 1165 | } 1166 | 1167 | _outputBufferOffset += bytesWritten; 1168 | 1169 | if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { 1170 | _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; 1171 | _outputBufferOffset = 0; 1172 | } 1173 | } 1174 | 1175 | if (_closeWhenFinishedWriting && 1176 | _outputBuffer.length - _outputBufferOffset == 0 && 1177 | (_inputStream.streamStatus != NSStreamStatusNotOpen && 1178 | _inputStream.streamStatus != NSStreamStatusClosed) && 1179 | !_sentClose) { 1180 | _sentClose = YES; 1181 | 1182 | @synchronized(self) { 1183 | [_outputStream close]; 1184 | [_inputStream close]; 1185 | 1186 | 1187 | for (NSArray *runLoop in [_scheduledRunloops copy]) { 1188 | [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; 1189 | } 1190 | } 1191 | 1192 | if (!_failed) { 1193 | [self _performDelegateBlock:^{ 1194 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1195 | [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; 1196 | } 1197 | }]; 1198 | } 1199 | 1200 | [self _scheduleCleanup]; 1201 | } 1202 | } 1203 | 1204 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; 1205 | { 1206 | [self assertOnWorkQueue]; 1207 | [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; 1208 | } 1209 | 1210 | - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1211 | { 1212 | [self assertOnWorkQueue]; 1213 | assert(dataLength); 1214 | 1215 | [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; 1216 | [self _pumpScanner]; 1217 | } 1218 | 1219 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; 1220 | { 1221 | [self assertOnWorkQueue]; 1222 | [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; 1223 | [self _pumpScanner]; 1224 | } 1225 | 1226 | 1227 | - (void)_scheduleCleanup 1228 | { 1229 | @synchronized(self) { 1230 | if (_cleanupScheduled) { 1231 | return; 1232 | } 1233 | 1234 | _cleanupScheduled = YES; 1235 | 1236 | // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves: 1237 | // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc 1238 | NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO]; 1239 | [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 1240 | } 1241 | } 1242 | 1243 | - (void)_cleanupSelfReference:(NSTimer *)timer 1244 | { 1245 | @synchronized(self) { 1246 | // Nuke NSStream delegate's 1247 | _inputStream.delegate = nil; 1248 | _outputStream.delegate = nil; 1249 | 1250 | // Remove the streams, right now, from the networkRunLoop 1251 | [_inputStream close]; 1252 | [_outputStream close]; 1253 | } 1254 | 1255 | // Cleanup selfRetain in the same GCD queue as usual 1256 | dispatch_async(_workQueue, ^{ 1257 | _selfRetain = nil; 1258 | }); 1259 | } 1260 | 1261 | 1262 | static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; 1263 | 1264 | - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; 1265 | { 1266 | [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; 1267 | } 1268 | 1269 | - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; 1270 | { 1271 | // TODO optimize so this can continue from where we last searched 1272 | stream_scanner consumer = ^size_t(NSData *data) { 1273 | __block size_t found_size = 0; 1274 | __block size_t match_count = 0; 1275 | 1276 | size_t size = data.length; 1277 | const unsigned char *buffer = data.bytes; 1278 | for (size_t i = 0; i < size; i++ ) { 1279 | if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { 1280 | match_count += 1; 1281 | if (match_count == length) { 1282 | found_size = i + 1; 1283 | break; 1284 | } 1285 | } else { 1286 | match_count = 0; 1287 | } 1288 | } 1289 | return found_size; 1290 | }; 1291 | [self _addConsumerWithScanner:consumer callback:dataHandler]; 1292 | } 1293 | 1294 | 1295 | // Returns true if did work 1296 | - (BOOL)_innerPumpScanner { 1297 | 1298 | BOOL didWork = NO; 1299 | 1300 | if (self.readyState >= SR_CLOSED) { 1301 | return didWork; 1302 | } 1303 | 1304 | if (!_consumers.count) { 1305 | return didWork; 1306 | } 1307 | 1308 | size_t curSize = _readBuffer.length - _readBufferOffset; 1309 | if (!curSize) { 1310 | return didWork; 1311 | } 1312 | 1313 | SRIOConsumer *consumer = [_consumers objectAtIndex:0]; 1314 | 1315 | size_t bytesNeeded = consumer.bytesNeeded; 1316 | 1317 | size_t foundSize = 0; 1318 | if (consumer.consumer) { 1319 | NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; 1320 | foundSize = consumer.consumer(tempView); 1321 | } else { 1322 | assert(consumer.bytesNeeded); 1323 | if (curSize >= bytesNeeded) { 1324 | foundSize = bytesNeeded; 1325 | } else if (consumer.readToCurrentFrame) { 1326 | foundSize = curSize; 1327 | } 1328 | } 1329 | 1330 | NSData *slice = nil; 1331 | if (consumer.readToCurrentFrame || foundSize) { 1332 | NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); 1333 | slice = [_readBuffer subdataWithRange:sliceRange]; 1334 | 1335 | _readBufferOffset += foundSize; 1336 | 1337 | if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { 1338 | _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; 1339 | } 1340 | 1341 | if (consumer.unmaskBytes) { 1342 | NSMutableData *mutableSlice = [slice mutableCopy]; 1343 | 1344 | NSUInteger len = mutableSlice.length; 1345 | uint8_t *bytes = mutableSlice.mutableBytes; 1346 | 1347 | for (NSUInteger i = 0; i < len; i++) { 1348 | bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; 1349 | _currentReadMaskOffset += 1; 1350 | } 1351 | 1352 | slice = mutableSlice; 1353 | } 1354 | 1355 | if (consumer.readToCurrentFrame) { 1356 | [_currentFrameData appendData:slice]; 1357 | 1358 | _readOpCount += 1; 1359 | 1360 | if (_currentFrameOpcode == SROpCodeTextFrame) { 1361 | // Validate UTF8 stuff. 1362 | size_t currentDataSize = _currentFrameData.length; 1363 | if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { 1364 | // TODO: Optimize the crap out of this. Don't really have to copy all the data each time 1365 | 1366 | size_t scanSize = currentDataSize - _currentStringScanPosition; 1367 | 1368 | NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; 1369 | int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); 1370 | 1371 | if (valid_utf8_size == -1) { 1372 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 1373 | dispatch_async(_workQueue, ^{ 1374 | [self closeConnection]; 1375 | }); 1376 | return didWork; 1377 | } else { 1378 | _currentStringScanPosition += valid_utf8_size; 1379 | } 1380 | } 1381 | 1382 | } 1383 | 1384 | consumer.bytesNeeded -= foundSize; 1385 | 1386 | if (consumer.bytesNeeded == 0) { 1387 | [_consumers removeObjectAtIndex:0]; 1388 | consumer.handler(self, nil); 1389 | [_consumerPool returnConsumer:consumer]; 1390 | didWork = YES; 1391 | } 1392 | } else if (foundSize) { 1393 | [_consumers removeObjectAtIndex:0]; 1394 | consumer.handler(self, slice); 1395 | [_consumerPool returnConsumer:consumer]; 1396 | didWork = YES; 1397 | } 1398 | } 1399 | return didWork; 1400 | } 1401 | 1402 | -(void)_pumpScanner; 1403 | { 1404 | [self assertOnWorkQueue]; 1405 | 1406 | if (!_isPumping) { 1407 | _isPumping = YES; 1408 | } else { 1409 | return; 1410 | } 1411 | 1412 | while ([self _innerPumpScanner]) { 1413 | 1414 | } 1415 | 1416 | _isPumping = NO; 1417 | } 1418 | 1419 | //#define NOMASK 1420 | 1421 | static const size_t SRFrameHeaderOverhead = 32; 1422 | 1423 | - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; 1424 | { 1425 | [self assertOnWorkQueue]; 1426 | 1427 | if (nil == data) { 1428 | return; 1429 | } 1430 | 1431 | NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData"); 1432 | 1433 | size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; 1434 | 1435 | NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; 1436 | if (!frame) { 1437 | [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; 1438 | return; 1439 | } 1440 | uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; 1441 | 1442 | // set fin 1443 | frame_buffer[0] = SRFinMask | opcode; 1444 | 1445 | BOOL useMask = YES; 1446 | #ifdef NOMASK 1447 | useMask = NO; 1448 | #endif 1449 | 1450 | if (useMask) { 1451 | // set the mask and header 1452 | frame_buffer[1] |= SRMaskMask; 1453 | } 1454 | 1455 | size_t frame_buffer_size = 2; 1456 | 1457 | const uint8_t *unmasked_payload = NULL; 1458 | if ([data isKindOfClass:[NSData class]]) { 1459 | unmasked_payload = (uint8_t *)[data bytes]; 1460 | } else if ([data isKindOfClass:[NSString class]]) { 1461 | unmasked_payload = (const uint8_t *)[data UTF8String]; 1462 | } else { 1463 | return; 1464 | } 1465 | 1466 | if (payloadLength < 126) { 1467 | frame_buffer[1] |= payloadLength; 1468 | } else if (payloadLength <= UINT16_MAX) { 1469 | frame_buffer[1] |= 126; 1470 | *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); 1471 | frame_buffer_size += sizeof(uint16_t); 1472 | } else { 1473 | frame_buffer[1] |= 127; 1474 | *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); 1475 | frame_buffer_size += sizeof(uint64_t); 1476 | } 1477 | 1478 | if (!useMask) { 1479 | for (size_t i = 0; i < payloadLength; i++) { 1480 | frame_buffer[frame_buffer_size] = unmasked_payload[i]; 1481 | frame_buffer_size += 1; 1482 | } 1483 | } else { 1484 | uint8_t *mask_key = frame_buffer + frame_buffer_size; 1485 | SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); 1486 | frame_buffer_size += sizeof(uint32_t); 1487 | 1488 | // TODO: could probably optimize this with SIMD 1489 | for (size_t i = 0; i < payloadLength; i++) { 1490 | frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; 1491 | frame_buffer_size += 1; 1492 | } 1493 | } 1494 | 1495 | assert(frame_buffer_size <= [frame length]); 1496 | frame.length = frame_buffer_size; 1497 | 1498 | [self _writeData:frame]; 1499 | } 1500 | 1501 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; 1502 | { 1503 | __weak typeof(self) weakSelf = self; 1504 | 1505 | if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { 1506 | 1507 | NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; 1508 | if (sslCerts) { 1509 | SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; 1510 | if (secTrust) { 1511 | NSInteger numCerts = SecTrustGetCertificateCount(secTrust); 1512 | for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { 1513 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); 1514 | NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); 1515 | 1516 | for (id ref in sslCerts) { 1517 | SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; 1518 | NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); 1519 | 1520 | if ([trustedCertData isEqualToData:certData]) { 1521 | _pinnedCertFound = YES; 1522 | break; 1523 | } 1524 | } 1525 | } 1526 | } 1527 | 1528 | if (!_pinnedCertFound) { 1529 | dispatch_async(_workQueue, ^{ 1530 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" }; 1531 | [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]]; 1532 | }); 1533 | return; 1534 | } else if (aStream == _outputStream) { 1535 | dispatch_async(_workQueue, ^{ 1536 | [self didConnect]; 1537 | }); 1538 | } 1539 | } 1540 | } 1541 | 1542 | dispatch_async(_workQueue, ^{ 1543 | [weakSelf safeHandleEvent:eventCode stream:aStream]; 1544 | }); 1545 | } 1546 | 1547 | - (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream 1548 | { 1549 | switch (eventCode) { 1550 | case NSStreamEventOpenCompleted: { 1551 | SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); 1552 | if (self.readyState >= SR_CLOSING) { 1553 | return; 1554 | } 1555 | assert(_readBuffer); 1556 | 1557 | // didConnect fires after certificate verification if we're using pinned certificates. 1558 | BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0; 1559 | if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) { 1560 | [self didConnect]; 1561 | } 1562 | [self _pumpWriting]; 1563 | [self _pumpScanner]; 1564 | break; 1565 | } 1566 | 1567 | case NSStreamEventErrorOccurred: { 1568 | SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); 1569 | /// TODO specify error better! 1570 | [self _failWithError:aStream.streamError]; 1571 | _readBufferOffset = 0; 1572 | [_readBuffer setLength:0]; 1573 | break; 1574 | 1575 | } 1576 | 1577 | case NSStreamEventEndEncountered: { 1578 | [self _pumpScanner]; 1579 | SRFastLog(@"NSStreamEventEndEncountered %@", aStream); 1580 | if (aStream.streamError) { 1581 | [self _failWithError:aStream.streamError]; 1582 | } else { 1583 | dispatch_async(_workQueue, ^{ 1584 | if (self.readyState != SR_CLOSED) { 1585 | self.readyState = SR_CLOSED; 1586 | [self _scheduleCleanup]; 1587 | } 1588 | 1589 | if (!_sentClose && !_failed) { 1590 | _sentClose = YES; 1591 | // If we get closed in this state it's probably not clean because we should be sending this when we send messages 1592 | [self _performDelegateBlock:^{ 1593 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1594 | [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; 1595 | } 1596 | }]; 1597 | } 1598 | }); 1599 | } 1600 | 1601 | break; 1602 | } 1603 | 1604 | case NSStreamEventHasBytesAvailable: { 1605 | SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); 1606 | const int bufferSize = 2048; 1607 | uint8_t buffer[bufferSize]; 1608 | 1609 | while (_inputStream.hasBytesAvailable) { 1610 | NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize]; 1611 | 1612 | if (bytes_read > 0) { 1613 | [_readBuffer appendBytes:buffer length:bytes_read]; 1614 | } else if (bytes_read < 0) { 1615 | [self _failWithError:_inputStream.streamError]; 1616 | } 1617 | 1618 | if (bytes_read != bufferSize) { 1619 | break; 1620 | } 1621 | }; 1622 | [self _pumpScanner]; 1623 | break; 1624 | } 1625 | 1626 | case NSStreamEventHasSpaceAvailable: { 1627 | SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); 1628 | [self _pumpWriting]; 1629 | break; 1630 | } 1631 | 1632 | default: 1633 | SRFastLog(@"(default) %@", aStream); 1634 | break; 1635 | } 1636 | } 1637 | 1638 | @end 1639 | 1640 | 1641 | @implementation SRIOConsumer 1642 | 1643 | @synthesize bytesNeeded = _bytesNeeded; 1644 | @synthesize consumer = _scanner; 1645 | @synthesize handler = _handler; 1646 | @synthesize readToCurrentFrame = _readToCurrentFrame; 1647 | @synthesize unmaskBytes = _unmaskBytes; 1648 | 1649 | - (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1650 | { 1651 | _scanner = [scanner copy]; 1652 | _handler = [handler copy]; 1653 | _bytesNeeded = bytesNeeded; 1654 | _readToCurrentFrame = readToCurrentFrame; 1655 | _unmaskBytes = unmaskBytes; 1656 | assert(_scanner || _bytesNeeded); 1657 | } 1658 | 1659 | 1660 | @end 1661 | 1662 | 1663 | @implementation SRIOConsumerPool { 1664 | NSUInteger _poolSize; 1665 | NSMutableArray *_bufferedConsumers; 1666 | } 1667 | 1668 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 1669 | { 1670 | self = [super init]; 1671 | if (self) { 1672 | _poolSize = poolSize; 1673 | _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; 1674 | } 1675 | return self; 1676 | } 1677 | 1678 | - (id)init 1679 | { 1680 | return [self initWithBufferCapacity:8]; 1681 | } 1682 | 1683 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1684 | { 1685 | SRIOConsumer *consumer = nil; 1686 | if (_bufferedConsumers.count) { 1687 | consumer = [_bufferedConsumers lastObject]; 1688 | [_bufferedConsumers removeLastObject]; 1689 | } else { 1690 | consumer = [[SRIOConsumer alloc] init]; 1691 | } 1692 | 1693 | [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; 1694 | 1695 | return consumer; 1696 | } 1697 | 1698 | - (void)returnConsumer:(SRIOConsumer *)consumer; 1699 | { 1700 | if (_bufferedConsumers.count < _poolSize) { 1701 | [_bufferedConsumers addObject:consumer]; 1702 | } 1703 | } 1704 | 1705 | @end 1706 | 1707 | 1708 | @implementation NSURLRequest (SRCertificateAdditions) 1709 | 1710 | - (NSArray *)SR_SSLPinnedCertificates; 1711 | { 1712 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1713 | } 1714 | 1715 | @end 1716 | 1717 | @implementation NSMutableURLRequest (SRCertificateAdditions) 1718 | 1719 | - (NSArray *)SR_SSLPinnedCertificates; 1720 | { 1721 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1722 | } 1723 | 1724 | - (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; 1725 | { 1726 | [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1727 | } 1728 | 1729 | @end 1730 | 1731 | @implementation NSURL (SRWebSocket) 1732 | 1733 | - (NSString *)SR_origin; 1734 | { 1735 | NSString *scheme = [self.scheme lowercaseString]; 1736 | 1737 | if ([scheme isEqualToString:@"wss"]) { 1738 | scheme = @"https"; 1739 | } else if ([scheme isEqualToString:@"ws"]) { 1740 | scheme = @"http"; 1741 | } 1742 | 1743 | BOOL portIsDefault = !self.port || 1744 | ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) || 1745 | ([scheme isEqualToString:@"https"] && self.port.integerValue == 443); 1746 | 1747 | if (!portIsDefault) { 1748 | return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port]; 1749 | } else { 1750 | return [NSString stringWithFormat:@"%@://%@", scheme, self.host]; 1751 | } 1752 | } 1753 | 1754 | @end 1755 | 1756 | //#define SR_ENABLE_LOG 1757 | 1758 | static inline void SRFastLog(NSString *format, ...) { 1759 | #ifdef SR_ENABLE_LOG 1760 | __block va_list arg_list; 1761 | va_start (arg_list, format); 1762 | 1763 | NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; 1764 | 1765 | va_end(arg_list); 1766 | 1767 | NSLog(@"[SR] %@", formattedString); 1768 | #endif 1769 | } 1770 | 1771 | 1772 | #ifdef HAS_ICU 1773 | 1774 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1775 | if ([data length] > INT32_MAX) { 1776 | // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. 1777 | return -1; 1778 | } 1779 | 1780 | int32_t size = (int32_t)[data length]; 1781 | 1782 | const void * contents = [data bytes]; 1783 | const uint8_t *str = (const uint8_t *)contents; 1784 | 1785 | UChar32 codepoint = 1; 1786 | int32_t offset = 0; 1787 | int32_t lastOffset = 0; 1788 | while(offset < size && codepoint > 0) { 1789 | lastOffset = offset; 1790 | U8_NEXT(str, offset, size, codepoint); 1791 | } 1792 | 1793 | if (codepoint == -1) { 1794 | // Check to see if the last byte is valid or whether it was just continuing 1795 | if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { 1796 | 1797 | size = -1; 1798 | } else { 1799 | uint8_t leadByte = str[lastOffset]; 1800 | U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); 1801 | 1802 | for (int i = lastOffset + 1; i < offset; i++) { 1803 | if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { 1804 | size = -1; 1805 | } 1806 | } 1807 | 1808 | if (size != -1) { 1809 | size = lastOffset; 1810 | } 1811 | } 1812 | } 1813 | 1814 | if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { 1815 | size = -1; 1816 | } 1817 | 1818 | return size; 1819 | } 1820 | 1821 | #else 1822 | 1823 | // This is a hack, and probably not optimal 1824 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1825 | static const int maxCodepointSize = 3; 1826 | 1827 | for (int i = 0; i < maxCodepointSize; i++) { 1828 | NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; 1829 | if (str) { 1830 | return (int32_t)data.length - i; 1831 | } 1832 | } 1833 | 1834 | return -1; 1835 | } 1836 | 1837 | #endif 1838 | 1839 | static _SRRunLoopThread *networkThread = nil; 1840 | static NSRunLoop *networkRunLoop = nil; 1841 | 1842 | @implementation NSRunLoop (SRWebSocket) 1843 | 1844 | + (NSRunLoop *)SR_networkRunLoop { 1845 | static dispatch_once_t onceToken; 1846 | dispatch_once(&onceToken, ^{ 1847 | networkThread = [[_SRRunLoopThread alloc] init]; 1848 | networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; 1849 | [networkThread start]; 1850 | networkRunLoop = networkThread.runLoop; 1851 | }); 1852 | 1853 | return networkRunLoop; 1854 | } 1855 | 1856 | @end 1857 | 1858 | 1859 | @implementation _SRRunLoopThread { 1860 | dispatch_group_t _waitGroup; 1861 | } 1862 | 1863 | @synthesize runLoop = _runLoop; 1864 | 1865 | - (void)dealloc 1866 | { 1867 | sr_dispatch_release(_waitGroup); 1868 | } 1869 | 1870 | - (id)init 1871 | { 1872 | self = [super init]; 1873 | if (self) { 1874 | _waitGroup = dispatch_group_create(); 1875 | dispatch_group_enter(_waitGroup); 1876 | } 1877 | return self; 1878 | } 1879 | 1880 | - (void)main; 1881 | { 1882 | @autoreleasepool { 1883 | _runLoop = [NSRunLoop currentRunLoop]; 1884 | dispatch_group_leave(_waitGroup); 1885 | 1886 | // Add an empty run loop source to prevent runloop from spinning. 1887 | CFRunLoopSourceContext sourceCtx = { 1888 | .version = 0, 1889 | .info = NULL, 1890 | .retain = NULL, 1891 | .release = NULL, 1892 | .copyDescription = NULL, 1893 | .equal = NULL, 1894 | .hash = NULL, 1895 | .schedule = NULL, 1896 | .cancel = NULL, 1897 | .perform = NULL 1898 | }; 1899 | CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); 1900 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 1901 | CFRelease(source); 1902 | 1903 | while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { 1904 | 1905 | } 1906 | assert(NO); 1907 | } 1908 | } 1909 | 1910 | - (NSRunLoop *)runLoop; 1911 | { 1912 | dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); 1913 | return _runLoop; 1914 | } 1915 | 1916 | @end 1917 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/SocketRocket/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SocketRocket 5 | 6 | 7 | Copyright 2012 Square Inc. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | 22 | Generated by CocoaPods - https://cocoapods.org 23 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | 18 | Copyright 2012 Square Inc. 19 | 20 | Licensed under the Apache License, Version 2.0 (the "License"); 21 | you may not use this file except in compliance with the License. 22 | You may obtain a copy of the License at 23 | 24 | http://www.apache.org/licenses/LICENSE-2.0 25 | 26 | Unless required by applicable law or agreed to in writing, software 27 | distributed under the License is distributed on an "AS IS" BASIS, 28 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 | See the License for the specific language governing permissions and 30 | limitations under the License. 31 | 32 | 33 | License 34 | Apache License, Version 2.0 35 | Title 36 | SocketRocket 37 | Type 38 | PSGroupSpecifier 39 | 40 | 41 | FooterText 42 | Generated by CocoaPods - https://cocoapods.org 43 | Title 44 | 45 | Type 46 | PSGroupSpecifier 47 | 48 | 49 | StringsTable 50 | Acknowledgements 51 | Title 52 | Acknowledgements 53 | 54 | 55 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_WebSocketDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_WebSocketDemo 5 | @end 6 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 42 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 43 | ;; 44 | *.xib) 45 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 46 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | 77 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 78 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 79 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 80 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | fi 83 | rm -f "$RESOURCES_TO_COPY" 84 | 85 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 86 | then 87 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 88 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 89 | while read line; do 90 | if [[ $line != "${PODS_ROOT}*" ]]; then 91 | XCASSET_FILES+=("$line") 92 | fi 93 | done <<<"$OTHER_XCASSETS" 94 | 95 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | fi 97 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SocketRocket" 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SocketRocket" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"SocketRocket" -l"icucore" -framework "CFNetwork" -framework "Security" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SocketRocket" 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SocketRocket" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"SocketRocket" -l"icucore" -framework "CFNetwork" -framework "Security" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SocketRocket : NSObject 3 | @end 4 | @implementation PodsDummy_SocketRocket 5 | @end 6 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /WebSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SocketRocket 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SocketRocket" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 4 | OTHER_LDFLAGS = -l"icucore" -framework "CFNetwork" -framework "Security" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7B5301731E557E7400074E6B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5301721E557E7400074E6B /* main.m */; }; 11 | 7B5301761E557E7400074E6B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5301751E557E7400074E6B /* AppDelegate.m */; }; 12 | 7B5301791E557E7400074E6B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5301781E557E7400074E6B /* ViewController.m */; }; 13 | 7B53017C1E557E7400074E6B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B53017A1E557E7400074E6B /* Main.storyboard */; }; 14 | 7B53017E1E557E7400074E6B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7B53017D1E557E7400074E6B /* Assets.xcassets */; }; 15 | 7B5301811E557E7400074E6B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B53017F1E557E7400074E6B /* LaunchScreen.storyboard */; }; 16 | 7B53018C1E557E7400074E6B /* WebSocketDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B53018B1E557E7400074E6B /* WebSocketDemoTests.m */; }; 17 | 7B5301971E557E7400074E6B /* WebSocketDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5301961E557E7400074E6B /* WebSocketDemoUITests.m */; }; 18 | 7B9AC0951F3C2BEE00E0D690 /* SocketRocketUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9AC0941F3C2BEE00E0D690 /* SocketRocketUtility.m */; }; 19 | BFC23A5617936DDE1B79CF60 /* libPods-WebSocketDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C3359F40715DED21C357E44 /* libPods-WebSocketDemo.a */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 7B5301881E557E7400074E6B /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 7B5301661E557E7300074E6B /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 7B53016D1E557E7400074E6B; 28 | remoteInfo = WebSocketDemo; 29 | }; 30 | 7B5301931E557E7400074E6B /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 7B5301661E557E7300074E6B /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 7B53016D1E557E7400074E6B; 35 | remoteInfo = WebSocketDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 5C3359F40715DED21C357E44 /* libPods-WebSocketDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WebSocketDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 7B53016E1E557E7400074E6B /* WebSocketDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebSocketDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 7B5301721E557E7400074E6B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 43 | 7B5301741E557E7400074E6B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 44 | 7B5301751E557E7400074E6B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 45 | 7B5301771E557E7400074E6B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | 7B5301781E557E7400074E6B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | 7B53017B1E557E7400074E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 7B53017D1E557E7400074E6B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 7B5301801E557E7400074E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 7B5301821E557E7400074E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 7B5301871E557E7400074E6B /* WebSocketDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebSocketDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 7B53018B1E557E7400074E6B /* WebSocketDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebSocketDemoTests.m; sourceTree = ""; }; 53 | 7B53018D1E557E7400074E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 7B5301921E557E7400074E6B /* WebSocketDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebSocketDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 7B5301961E557E7400074E6B /* WebSocketDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebSocketDemoUITests.m; sourceTree = ""; }; 56 | 7B5301981E557E7400074E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 7B9AC0931F3C2BEE00E0D690 /* SocketRocketUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocketUtility.h; sourceTree = ""; }; 58 | 7B9AC0941F3C2BEE00E0D690 /* SocketRocketUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketRocketUtility.m; sourceTree = ""; }; 59 | D061913CD0F734A111F3198E /* Pods-WebSocketDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebSocketDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo.release.xcconfig"; sourceTree = ""; }; 60 | F6F4A3807BB9701FBE8A0869 /* Pods-WebSocketDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebSocketDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo.debug.xcconfig"; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 7B53016B1E557E7400074E6B /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | BFC23A5617936DDE1B79CF60 /* libPods-WebSocketDemo.a in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | 7B5301841E557E7400074E6B /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 7B53018F1E557E7400074E6B /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 51CC4C12D1448FE3C80AE4A2 /* Frameworks */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 5C3359F40715DED21C357E44 /* libPods-WebSocketDemo.a */, 93 | ); 94 | name = Frameworks; 95 | sourceTree = ""; 96 | }; 97 | 7B5301651E557E7300074E6B = { 98 | isa = PBXGroup; 99 | children = ( 100 | 7B5301701E557E7400074E6B /* WebSocketDemo */, 101 | 7B53018A1E557E7400074E6B /* WebSocketDemoTests */, 102 | 7B5301951E557E7400074E6B /* WebSocketDemoUITests */, 103 | 7B53016F1E557E7400074E6B /* Products */, 104 | BB4423638CB34E205C151A34 /* Pods */, 105 | 51CC4C12D1448FE3C80AE4A2 /* Frameworks */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | 7B53016F1E557E7400074E6B /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 7B53016E1E557E7400074E6B /* WebSocketDemo.app */, 113 | 7B5301871E557E7400074E6B /* WebSocketDemoTests.xctest */, 114 | 7B5301921E557E7400074E6B /* WebSocketDemoUITests.xctest */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 7B5301701E557E7400074E6B /* WebSocketDemo */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 7B9AC0921F3C2BEE00E0D690 /* WebSocket */, 123 | 7B5301741E557E7400074E6B /* AppDelegate.h */, 124 | 7B5301751E557E7400074E6B /* AppDelegate.m */, 125 | 7B5301771E557E7400074E6B /* ViewController.h */, 126 | 7B5301781E557E7400074E6B /* ViewController.m */, 127 | 7B53017A1E557E7400074E6B /* Main.storyboard */, 128 | 7B53017D1E557E7400074E6B /* Assets.xcassets */, 129 | 7B53017F1E557E7400074E6B /* LaunchScreen.storyboard */, 130 | 7B5301821E557E7400074E6B /* Info.plist */, 131 | 7B5301711E557E7400074E6B /* Supporting Files */, 132 | ); 133 | path = WebSocketDemo; 134 | sourceTree = ""; 135 | }; 136 | 7B5301711E557E7400074E6B /* Supporting Files */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 7B5301721E557E7400074E6B /* main.m */, 140 | ); 141 | name = "Supporting Files"; 142 | sourceTree = ""; 143 | }; 144 | 7B53018A1E557E7400074E6B /* WebSocketDemoTests */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 7B53018B1E557E7400074E6B /* WebSocketDemoTests.m */, 148 | 7B53018D1E557E7400074E6B /* Info.plist */, 149 | ); 150 | path = WebSocketDemoTests; 151 | sourceTree = ""; 152 | }; 153 | 7B5301951E557E7400074E6B /* WebSocketDemoUITests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 7B5301961E557E7400074E6B /* WebSocketDemoUITests.m */, 157 | 7B5301981E557E7400074E6B /* Info.plist */, 158 | ); 159 | path = WebSocketDemoUITests; 160 | sourceTree = ""; 161 | }; 162 | 7B9AC0921F3C2BEE00E0D690 /* WebSocket */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 7B9AC0931F3C2BEE00E0D690 /* SocketRocketUtility.h */, 166 | 7B9AC0941F3C2BEE00E0D690 /* SocketRocketUtility.m */, 167 | ); 168 | path = WebSocket; 169 | sourceTree = ""; 170 | }; 171 | BB4423638CB34E205C151A34 /* Pods */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | F6F4A3807BB9701FBE8A0869 /* Pods-WebSocketDemo.debug.xcconfig */, 175 | D061913CD0F734A111F3198E /* Pods-WebSocketDemo.release.xcconfig */, 176 | ); 177 | name = Pods; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXGroup section */ 181 | 182 | /* Begin PBXNativeTarget section */ 183 | 7B53016D1E557E7400074E6B /* WebSocketDemo */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 7B53019B1E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemo" */; 186 | buildPhases = ( 187 | 3EF5E531AA02395386E08C06 /* [CP] Check Pods Manifest.lock */, 188 | 7B53016A1E557E7400074E6B /* Sources */, 189 | 7B53016B1E557E7400074E6B /* Frameworks */, 190 | 7B53016C1E557E7400074E6B /* Resources */, 191 | C8F0AF07A1E9A8072C822CAB /* [CP] Embed Pods Frameworks */, 192 | 6C2DEB9D5CF0DFE3EEDFD194 /* [CP] Copy Pods Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | ); 198 | name = WebSocketDemo; 199 | productName = WebSocketDemo; 200 | productReference = 7B53016E1E557E7400074E6B /* WebSocketDemo.app */; 201 | productType = "com.apple.product-type.application"; 202 | }; 203 | 7B5301861E557E7400074E6B /* WebSocketDemoTests */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = 7B53019E1E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemoTests" */; 206 | buildPhases = ( 207 | 7B5301831E557E7400074E6B /* Sources */, 208 | 7B5301841E557E7400074E6B /* Frameworks */, 209 | 7B5301851E557E7400074E6B /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | 7B5301891E557E7400074E6B /* PBXTargetDependency */, 215 | ); 216 | name = WebSocketDemoTests; 217 | productName = WebSocketDemoTests; 218 | productReference = 7B5301871E557E7400074E6B /* WebSocketDemoTests.xctest */; 219 | productType = "com.apple.product-type.bundle.unit-test"; 220 | }; 221 | 7B5301911E557E7400074E6B /* WebSocketDemoUITests */ = { 222 | isa = PBXNativeTarget; 223 | buildConfigurationList = 7B5301A11E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemoUITests" */; 224 | buildPhases = ( 225 | 7B53018E1E557E7400074E6B /* Sources */, 226 | 7B53018F1E557E7400074E6B /* Frameworks */, 227 | 7B5301901E557E7400074E6B /* Resources */, 228 | ); 229 | buildRules = ( 230 | ); 231 | dependencies = ( 232 | 7B5301941E557E7400074E6B /* PBXTargetDependency */, 233 | ); 234 | name = WebSocketDemoUITests; 235 | productName = WebSocketDemoUITests; 236 | productReference = 7B5301921E557E7400074E6B /* WebSocketDemoUITests.xctest */; 237 | productType = "com.apple.product-type.bundle.ui-testing"; 238 | }; 239 | /* End PBXNativeTarget section */ 240 | 241 | /* Begin PBXProject section */ 242 | 7B5301661E557E7300074E6B /* Project object */ = { 243 | isa = PBXProject; 244 | attributes = { 245 | LastUpgradeCheck = 0820; 246 | ORGANIZATIONNAME = newbike; 247 | TargetAttributes = { 248 | 7B53016D1E557E7400074E6B = { 249 | CreatedOnToolsVersion = 8.2.1; 250 | DevelopmentTeam = 87A4WPHHG4; 251 | ProvisioningStyle = Automatic; 252 | }; 253 | 7B5301861E557E7400074E6B = { 254 | CreatedOnToolsVersion = 8.2.1; 255 | ProvisioningStyle = Automatic; 256 | TestTargetID = 7B53016D1E557E7400074E6B; 257 | }; 258 | 7B5301911E557E7400074E6B = { 259 | CreatedOnToolsVersion = 8.2.1; 260 | ProvisioningStyle = Automatic; 261 | TestTargetID = 7B53016D1E557E7400074E6B; 262 | }; 263 | }; 264 | }; 265 | buildConfigurationList = 7B5301691E557E7300074E6B /* Build configuration list for PBXProject "WebSocketDemo" */; 266 | compatibilityVersion = "Xcode 3.2"; 267 | developmentRegion = English; 268 | hasScannedForEncodings = 0; 269 | knownRegions = ( 270 | en, 271 | Base, 272 | ); 273 | mainGroup = 7B5301651E557E7300074E6B; 274 | productRefGroup = 7B53016F1E557E7400074E6B /* Products */; 275 | projectDirPath = ""; 276 | projectRoot = ""; 277 | targets = ( 278 | 7B53016D1E557E7400074E6B /* WebSocketDemo */, 279 | 7B5301861E557E7400074E6B /* WebSocketDemoTests */, 280 | 7B5301911E557E7400074E6B /* WebSocketDemoUITests */, 281 | ); 282 | }; 283 | /* End PBXProject section */ 284 | 285 | /* Begin PBXResourcesBuildPhase section */ 286 | 7B53016C1E557E7400074E6B /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 7B5301811E557E7400074E6B /* LaunchScreen.storyboard in Resources */, 291 | 7B53017E1E557E7400074E6B /* Assets.xcassets in Resources */, 292 | 7B53017C1E557E7400074E6B /* Main.storyboard in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 7B5301851E557E7400074E6B /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | 7B5301901E557E7400074E6B /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | /* End PBXResourcesBuildPhase section */ 311 | 312 | /* Begin PBXShellScriptBuildPhase section */ 313 | 3EF5E531AA02395386E08C06 /* [CP] Check Pods Manifest.lock */ = { 314 | isa = PBXShellScriptBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | ); 318 | inputPaths = ( 319 | ); 320 | name = "[CP] Check Pods Manifest.lock"; 321 | outputPaths = ( 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | shellPath = /bin/sh; 325 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 326 | showEnvVarsInLog = 0; 327 | }; 328 | 6C2DEB9D5CF0DFE3EEDFD194 /* [CP] Copy Pods Resources */ = { 329 | isa = PBXShellScriptBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | ); 333 | inputPaths = ( 334 | ); 335 | name = "[CP] Copy Pods Resources"; 336 | outputPaths = ( 337 | ); 338 | runOnlyForDeploymentPostprocessing = 0; 339 | shellPath = /bin/sh; 340 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-resources.sh\"\n"; 341 | showEnvVarsInLog = 0; 342 | }; 343 | C8F0AF07A1E9A8072C822CAB /* [CP] Embed Pods Frameworks */ = { 344 | isa = PBXShellScriptBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | ); 348 | inputPaths = ( 349 | ); 350 | name = "[CP] Embed Pods Frameworks"; 351 | outputPaths = ( 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | shellPath = /bin/sh; 355 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebSocketDemo/Pods-WebSocketDemo-frameworks.sh\"\n"; 356 | showEnvVarsInLog = 0; 357 | }; 358 | /* End PBXShellScriptBuildPhase section */ 359 | 360 | /* Begin PBXSourcesBuildPhase section */ 361 | 7B53016A1E557E7400074E6B /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 7B5301791E557E7400074E6B /* ViewController.m in Sources */, 366 | 7B9AC0951F3C2BEE00E0D690 /* SocketRocketUtility.m in Sources */, 367 | 7B5301761E557E7400074E6B /* AppDelegate.m in Sources */, 368 | 7B5301731E557E7400074E6B /* main.m in Sources */, 369 | ); 370 | runOnlyForDeploymentPostprocessing = 0; 371 | }; 372 | 7B5301831E557E7400074E6B /* Sources */ = { 373 | isa = PBXSourcesBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | 7B53018C1E557E7400074E6B /* WebSocketDemoTests.m in Sources */, 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | 7B53018E1E557E7400074E6B /* Sources */ = { 381 | isa = PBXSourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | 7B5301971E557E7400074E6B /* WebSocketDemoUITests.m in Sources */, 385 | ); 386 | runOnlyForDeploymentPostprocessing = 0; 387 | }; 388 | /* End PBXSourcesBuildPhase section */ 389 | 390 | /* Begin PBXTargetDependency section */ 391 | 7B5301891E557E7400074E6B /* PBXTargetDependency */ = { 392 | isa = PBXTargetDependency; 393 | target = 7B53016D1E557E7400074E6B /* WebSocketDemo */; 394 | targetProxy = 7B5301881E557E7400074E6B /* PBXContainerItemProxy */; 395 | }; 396 | 7B5301941E557E7400074E6B /* PBXTargetDependency */ = { 397 | isa = PBXTargetDependency; 398 | target = 7B53016D1E557E7400074E6B /* WebSocketDemo */; 399 | targetProxy = 7B5301931E557E7400074E6B /* PBXContainerItemProxy */; 400 | }; 401 | /* End PBXTargetDependency section */ 402 | 403 | /* Begin PBXVariantGroup section */ 404 | 7B53017A1E557E7400074E6B /* Main.storyboard */ = { 405 | isa = PBXVariantGroup; 406 | children = ( 407 | 7B53017B1E557E7400074E6B /* Base */, 408 | ); 409 | name = Main.storyboard; 410 | sourceTree = ""; 411 | }; 412 | 7B53017F1E557E7400074E6B /* LaunchScreen.storyboard */ = { 413 | isa = PBXVariantGroup; 414 | children = ( 415 | 7B5301801E557E7400074E6B /* Base */, 416 | ); 417 | name = LaunchScreen.storyboard; 418 | sourceTree = ""; 419 | }; 420 | /* End PBXVariantGroup section */ 421 | 422 | /* Begin XCBuildConfiguration section */ 423 | 7B5301991E557E7400074E6B /* Debug */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | ALWAYS_SEARCH_USER_PATHS = NO; 427 | CLANG_ANALYZER_NONNULL = YES; 428 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 429 | CLANG_CXX_LIBRARY = "libc++"; 430 | CLANG_ENABLE_MODULES = YES; 431 | CLANG_ENABLE_OBJC_ARC = YES; 432 | CLANG_WARN_BOOL_CONVERSION = YES; 433 | CLANG_WARN_CONSTANT_CONVERSION = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INFINITE_RECURSION = YES; 439 | CLANG_WARN_INT_CONVERSION = YES; 440 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = dwarf; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | ENABLE_TESTABILITY = YES; 449 | GCC_C_LANGUAGE_STANDARD = gnu99; 450 | GCC_DYNAMIC_NO_PIC = NO; 451 | GCC_NO_COMMON_BLOCKS = YES; 452 | GCC_OPTIMIZATION_LEVEL = 0; 453 | GCC_PREPROCESSOR_DEFINITIONS = ( 454 | "DEBUG=1", 455 | "$(inherited)", 456 | ); 457 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 458 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 459 | GCC_WARN_UNDECLARED_SELECTOR = YES; 460 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 461 | GCC_WARN_UNUSED_FUNCTION = YES; 462 | GCC_WARN_UNUSED_VARIABLE = YES; 463 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 464 | MTL_ENABLE_DEBUG_INFO = YES; 465 | ONLY_ACTIVE_ARCH = YES; 466 | SDKROOT = iphoneos; 467 | }; 468 | name = Debug; 469 | }; 470 | 7B53019A1E557E7400074E6B /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | ALWAYS_SEARCH_USER_PATHS = NO; 474 | CLANG_ANALYZER_NONNULL = YES; 475 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 476 | CLANG_CXX_LIBRARY = "libc++"; 477 | CLANG_ENABLE_MODULES = YES; 478 | CLANG_ENABLE_OBJC_ARC = YES; 479 | CLANG_WARN_BOOL_CONVERSION = YES; 480 | CLANG_WARN_CONSTANT_CONVERSION = YES; 481 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 482 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 483 | CLANG_WARN_EMPTY_BODY = YES; 484 | CLANG_WARN_ENUM_CONVERSION = YES; 485 | CLANG_WARN_INFINITE_RECURSION = YES; 486 | CLANG_WARN_INT_CONVERSION = YES; 487 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 488 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 489 | CLANG_WARN_UNREACHABLE_CODE = YES; 490 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 491 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 492 | COPY_PHASE_STRIP = NO; 493 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 494 | ENABLE_NS_ASSERTIONS = NO; 495 | ENABLE_STRICT_OBJC_MSGSEND = YES; 496 | GCC_C_LANGUAGE_STANDARD = gnu99; 497 | GCC_NO_COMMON_BLOCKS = YES; 498 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 499 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 500 | GCC_WARN_UNDECLARED_SELECTOR = YES; 501 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 502 | GCC_WARN_UNUSED_FUNCTION = YES; 503 | GCC_WARN_UNUSED_VARIABLE = YES; 504 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 505 | MTL_ENABLE_DEBUG_INFO = NO; 506 | SDKROOT = iphoneos; 507 | VALIDATE_PRODUCT = YES; 508 | }; 509 | name = Release; 510 | }; 511 | 7B53019C1E557E7400074E6B /* Debug */ = { 512 | isa = XCBuildConfiguration; 513 | baseConfigurationReference = F6F4A3807BB9701FBE8A0869 /* Pods-WebSocketDemo.debug.xcconfig */; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | DEVELOPMENT_TEAM = 87A4WPHHG4; 517 | INFOPLIST_FILE = WebSocketDemo/Info.plist; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 519 | PRODUCT_BUNDLE_IDENTIFIER = com.Sun.WebSocketDemo; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | }; 522 | name = Debug; 523 | }; 524 | 7B53019D1E557E7400074E6B /* Release */ = { 525 | isa = XCBuildConfiguration; 526 | baseConfigurationReference = D061913CD0F734A111F3198E /* Pods-WebSocketDemo.release.xcconfig */; 527 | buildSettings = { 528 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 529 | DEVELOPMENT_TEAM = 87A4WPHHG4; 530 | INFOPLIST_FILE = WebSocketDemo/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 532 | PRODUCT_BUNDLE_IDENTIFIER = com.Sun.WebSocketDemo; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | }; 535 | name = Release; 536 | }; 537 | 7B53019F1E557E7400074E6B /* Debug */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | BUNDLE_LOADER = "$(TEST_HOST)"; 541 | INFOPLIST_FILE = WebSocketDemoTests/Info.plist; 542 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 543 | PRODUCT_BUNDLE_IDENTIFIER = com.honestRice.WebSocketDemoTests; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSocketDemo.app/WebSocketDemo"; 546 | }; 547 | name = Debug; 548 | }; 549 | 7B5301A01E557E7400074E6B /* Release */ = { 550 | isa = XCBuildConfiguration; 551 | buildSettings = { 552 | BUNDLE_LOADER = "$(TEST_HOST)"; 553 | INFOPLIST_FILE = WebSocketDemoTests/Info.plist; 554 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.honestRice.WebSocketDemoTests; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSocketDemo.app/WebSocketDemo"; 558 | }; 559 | name = Release; 560 | }; 561 | 7B5301A21E557E7400074E6B /* Debug */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | INFOPLIST_FILE = WebSocketDemoUITests/Info.plist; 565 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 566 | PRODUCT_BUNDLE_IDENTIFIER = com.honestRice.WebSocketDemoUITests; 567 | PRODUCT_NAME = "$(TARGET_NAME)"; 568 | TEST_TARGET_NAME = WebSocketDemo; 569 | }; 570 | name = Debug; 571 | }; 572 | 7B5301A31E557E7400074E6B /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | INFOPLIST_FILE = WebSocketDemoUITests/Info.plist; 576 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 577 | PRODUCT_BUNDLE_IDENTIFIER = com.honestRice.WebSocketDemoUITests; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | TEST_TARGET_NAME = WebSocketDemo; 580 | }; 581 | name = Release; 582 | }; 583 | /* End XCBuildConfiguration section */ 584 | 585 | /* Begin XCConfigurationList section */ 586 | 7B5301691E557E7300074E6B /* Build configuration list for PBXProject "WebSocketDemo" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | 7B5301991E557E7400074E6B /* Debug */, 590 | 7B53019A1E557E7400074E6B /* Release */, 591 | ); 592 | defaultConfigurationIsVisible = 0; 593 | defaultConfigurationName = Release; 594 | }; 595 | 7B53019B1E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemo" */ = { 596 | isa = XCConfigurationList; 597 | buildConfigurations = ( 598 | 7B53019C1E557E7400074E6B /* Debug */, 599 | 7B53019D1E557E7400074E6B /* Release */, 600 | ); 601 | defaultConfigurationIsVisible = 0; 602 | defaultConfigurationName = Release; 603 | }; 604 | 7B53019E1E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemoTests" */ = { 605 | isa = XCConfigurationList; 606 | buildConfigurations = ( 607 | 7B53019F1E557E7400074E6B /* Debug */, 608 | 7B5301A01E557E7400074E6B /* Release */, 609 | ); 610 | defaultConfigurationIsVisible = 0; 611 | defaultConfigurationName = Release; 612 | }; 613 | 7B5301A11E557E7400074E6B /* Build configuration list for PBXNativeTarget "WebSocketDemoUITests" */ = { 614 | isa = XCConfigurationList; 615 | buildConfigurations = ( 616 | 7B5301A21E557E7400074E6B /* Debug */, 617 | 7B5301A31E557E7400074E6B /* Release */, 618 | ); 619 | defaultConfigurationIsVisible = 0; 620 | defaultConfigurationName = Release; 621 | }; 622 | /* End XCConfigurationList section */ 623 | }; 624 | rootObject = 7B5301661E557E7300074E6B /* Project object */; 625 | } 626 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcodeproj/project.xcworkspace/xcuserdata/sunjun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunsunsunJunn/SJWebSocketDemo/d8806fec4a7a65642c87183a329ce853295852d7/WebSocketDemo/WebSocketDemo.xcodeproj/project.xcworkspace/xcuserdata/sunjun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcodeproj/xcuserdata/sunjun.xcuserdatad/xcschemes/WebSocketDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcodeproj/xcuserdata/sunjun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WebSocketDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 7B53016D1E557E7400074E6B 16 | 17 | primary 18 | 19 | 20 | 7B5301861E557E7400074E6B 21 | 22 | primary 23 | 24 | 25 | 7B5301911E557E7400074E6B 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcworkspace/xcuserdata/sunjun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunsunsunJunn/SJWebSocketDemo/d8806fec4a7a65642c87183a329ce853295852d7/WebSocketDemo/WebSocketDemo.xcworkspace/xcuserdata/sunjun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo.xcworkspace/xcuserdata/sunjun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WebSocketDemo 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WebSocketDemo 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | 34 | } 35 | 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WebSocketDemo 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | disConnectByUser , 13 | disConnectByServer, 14 | } DisConnectType; 15 | 16 | @interface ViewController : UIViewController 17 | 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WebSocketDemo 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SocketRocketUtility.h" 11 | 12 | @interface ViewController () 13 | 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | [[SocketRocketUtility instance] SRWebSocketOpenWithURLString:@"ws://121.43.38.179:7397"]; 23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidOpen) name:kWebSocketDidOpenNote object:nil]; 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidReceiveMsg:) name:kWebSocketDidCloseNote object:nil]; 25 | 26 | 27 | // [[SocketRocketUtility instance] SRWebSocketClose]; 在需要得地方 关闭socket 28 | } 29 | 30 | - (void)SRWebSocketDidOpen { 31 | NSLog(@"开启成功"); 32 | //在成功后需要做的操作。。。 33 | 34 | } 35 | 36 | - (void)SRWebSocketDidReceiveMsg:(NSNotification *)note { 37 | //收到服务端发送过来的消息 38 | NSString * message = note.object; 39 | NSLog(@"%@",message); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/WebSocket/SocketRocketUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketRocketUtility.h 3 | // SUN 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 SUN. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern NSString * const kNeedPayOrderNote; 13 | extern NSString * const kWebSocketDidOpenNote; 14 | extern NSString * const kWebSocketDidCloseNote; 15 | extern NSString * const kWebSocketdidReceiveMessageNote; 16 | 17 | @interface SocketRocketUtility : NSObject 18 | 19 | /** 获取连接状态 */ 20 | @property (nonatomic,assign,readonly) SRReadyState socketReadyState; 21 | 22 | /** 开始连接 */ 23 | - (void)SRWebSocketOpenWithURLString:(NSString *)urlString; 24 | 25 | /** 关闭连接 */ 26 | - (void)SRWebSocketClose; 27 | 28 | /** 发送数据 */ 29 | - (void)sendData:(id)data; 30 | 31 | + (SocketRocketUtility *)instance; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/WebSocket/SocketRocketUtility.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketRocketUtility.m 3 | // SUN 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 SUN. All rights reserved. 7 | // 8 | 9 | #import "SocketRocketUtility.h" 10 | 11 | #define dispatch_main_async_safe(block)\ 12 | if ([NSThread isMainThread]) {\ 13 | block();\ 14 | } else {\ 15 | dispatch_async(dispatch_get_main_queue(), block);\ 16 | } 17 | 18 | NSString * const kNeedPayOrderNote = @"kNeedPayOrderNote"; 19 | NSString * const kWebSocketDidOpenNote = @"kWebSocketDidOpenNote"; 20 | NSString * const kWebSocketDidCloseNote = @"kWebSocketDidCloseNote"; 21 | NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote"; 22 | 23 | @interface SocketRocketUtility() 24 | { 25 | int _index; 26 | NSTimer * heartBeat; 27 | NSTimeInterval reConnectTime; 28 | } 29 | 30 | @property (nonatomic,strong) SRWebSocket *socket; 31 | 32 | @property (nonatomic,copy) NSString *urlString; 33 | 34 | @end 35 | 36 | @implementation SocketRocketUtility 37 | 38 | + (SocketRocketUtility *)instance { 39 | static SocketRocketUtility *Instance = nil; 40 | static dispatch_once_t predicate; 41 | dispatch_once(&predicate, ^{ 42 | Instance = [[SocketRocketUtility alloc] init]; 43 | }); 44 | return Instance; 45 | } 46 | 47 | #pragma mark - **************** public methods 48 | -(void)SRWebSocketOpenWithURLString:(NSString *)urlString { 49 | 50 | //如果是同一个url return 51 | if (self.socket) { 52 | return; 53 | } 54 | 55 | if (!urlString) { 56 | return; 57 | } 58 | 59 | self.urlString = urlString; 60 | 61 | self.socket = [[SRWebSocket alloc] initWithURLRequest: 62 | [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]]; 63 | 64 | NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString); 65 | 66 | //SRWebSocketDelegate 协议 67 | self.socket.delegate = self; 68 | 69 | //开始连接 70 | [self.socket open]; 71 | } 72 | 73 | - (void)SRWebSocketClose { 74 | if (self.socket){ 75 | [self.socket close]; 76 | self.socket = nil; 77 | //断开连接时销毁心跳 78 | [self destoryHeartBeat]; 79 | } 80 | } 81 | 82 | #define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self 83 | - (void)sendData:(id)data { 84 | NSLog(@"socketSendData --------------- %@",data); 85 | 86 | WeakSelf(ws); 87 | dispatch_queue_t queue = dispatch_queue_create("zy", NULL); 88 | 89 | dispatch_async(queue, ^{ 90 | if (weakSelf.socket != nil) { 91 | // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩 92 | if (weakSelf.socket.readyState == SR_OPEN) { 93 | [weakSelf.socket send:data]; // 发送数据 94 | 95 | } else if (weakSelf.socket.readyState == SR_CONNECTING) { 96 | NSLog(@"正在连接中,重连后其他方法会去自动同步数据"); 97 | // 每隔2秒检测一次 socket.readyState 状态,检测 10 次左右 98 | // 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据 99 | // 如果 10 次都还是没连上的,那这个发送请求就丢失了,这种情况是服务器的问题了,小概率的 100 | // 代码有点长,我就写个逻辑在这里好了 101 | [self reConnect]; 102 | 103 | } else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) { 104 | // websocket 断开了,调用 reConnect 方法重连 105 | 106 | NSLog(@"重连"); 107 | 108 | [self reConnect]; 109 | } 110 | } else { 111 | NSLog(@"没网络,发送失败,一旦断网 socket 会被我设置 nil 的"); 112 | NSLog(@"其实最好是发送前判断一下网络状态比较好,我写的有点晦涩,socket==nil来表示断网"); 113 | } 114 | }); 115 | } 116 | 117 | #pragma mark - **************** private mothodes 118 | //重连机制 119 | - (void)reConnect { 120 | [self SRWebSocketClose]; 121 | 122 | //超过一分钟就不再重连 所以只会重连5次 2^5 = 64 123 | if (reConnectTime > 64) { 124 | //您的网络状况不是很好,请检查网络后重试 125 | return; 126 | } 127 | 128 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 129 | self.socket = nil; 130 | [self SRWebSocketOpenWithURLString:self.urlString]; 131 | NSLog(@"重连"); 132 | }); 133 | 134 | //重连时间2的指数级增长 135 | if (reConnectTime == 0) { 136 | reConnectTime = 2; 137 | } else { 138 | reConnectTime *= 2; 139 | } 140 | } 141 | 142 | 143 | //取消心跳 144 | - (void)destoryHeartBeat { 145 | dispatch_main_async_safe(^{ 146 | if (heartBeat) { 147 | if ([heartBeat respondsToSelector:@selector(isValid)]){ 148 | if ([heartBeat isValid]){ 149 | [heartBeat invalidate]; 150 | heartBeat = nil; 151 | } 152 | } 153 | } 154 | }) 155 | } 156 | 157 | //初始化心跳 158 | - (void)initHeartBeat { 159 | dispatch_main_async_safe(^{ 160 | [self destoryHeartBeat]; 161 | //心跳设置为3分钟,NAT超时一般为5分钟 162 | heartBeat = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(sentheart) userInfo:nil repeats:YES]; 163 | //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小 164 | [[NSRunLoop currentRunLoop] addTimer:heartBeat forMode:NSRunLoopCommonModes]; 165 | }) 166 | } 167 | 168 | - (void)sentheart { 169 | //发送心跳 和后台可以约定发送什么内容 一般可以调用ping 我这里根据后台的要求 发送了data给他 170 | [self sendData:@"heart"]; 171 | } 172 | 173 | //pingPong 174 | - (void)ping { 175 | if (self.socket.readyState == SR_OPEN) { 176 | [self.socket sendPing:nil]; 177 | } 178 | } 179 | 180 | #pragma mark - **************** SRWebSocketDelegate 181 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket { 182 | //每次正常连接的时候清零重连时间 183 | reConnectTime = 0; 184 | //开启心跳 185 | [self initHeartBeat]; 186 | if (webSocket == self.socket) { 187 | NSLog(@"************************** socket 连接成功************************** "); 188 | [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidOpenNote object:nil]; 189 | } 190 | } 191 | 192 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { 193 | if (webSocket == self.socket) { 194 | NSLog(@"************************** socket 连接失败************************** "); 195 | _socket = nil; 196 | //连接失败就重连 197 | [self reConnect]; 198 | } 199 | } 200 | 201 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { 202 | if (webSocket == self.socket) { 203 | NSLog(@"************************** socket连接断开************************** "); 204 | NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean); 205 | [self SRWebSocketClose]; 206 | [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidCloseNote object:nil]; 207 | } 208 | } 209 | 210 | /* 211 | 该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的, 212 | 在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包, 213 | 用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息, 214 | 我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的 215 | */ 216 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload { 217 | NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding]; 218 | NSLog(@"reply===%@",reply); 219 | } 220 | 221 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { 222 | 223 | if (webSocket == self.socket) { 224 | NSLog(@"************************** socket收到数据了************************** "); 225 | NSLog(@"我这后台约定的 message 是 json 格式数据收到数据,就按格式解析吧,然后把数据发给调用层"); 226 | NSLog(@"message:%@",message); 227 | 228 | [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message]; 229 | } 230 | } 231 | 232 | #pragma mark - **************** setter getter 233 | - (SRReadyState)socketReadyState { 234 | return self.socket.readyState; 235 | } 236 | 237 | - (void)dealloc { 238 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 239 | } 240 | 241 | 242 | @end 243 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WebSocketDemo 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemoTests/WebSocketDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebSocketDemoTests.m 3 | // WebSocketDemoTests 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WebSocketDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WebSocketDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WebSocketDemo/WebSocketDemoUITests/WebSocketDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebSocketDemoUITests.m 3 | // WebSocketDemoUITests 4 | // 5 | // Created by 孙俊 on 17/2/16. 6 | // Copyright © 2017年 newbike. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WebSocketDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WebSocketDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------