├── LICENSE ├── README.md └── socketio ├── socketio.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── socketio.xccheckout │ └── xcuserdata │ │ └── derekkinsman.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── derekkinsman.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── socketio.xcscheme │ └── xcschememanagement.plist ├── socketio ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── SocketRocket │ ├── SRWebSocket.h │ ├── SRWebSocket.m │ └── SocketRocket-Prefix.pch ├── ViewController.swift └── socketio-Bridging-Header.h └── socketioTests ├── Info.plist └── socketioTests.swift /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Teehan+Lax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SwiftSocketIODemo 2 | ================= 3 | 4 | Swift & SocketIO Demo 5 | 6 | In socketio/ViewController.swift change server on line 15 to point to your own server running Node. -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0E441D2D199939EA001A5905 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E441D2C199939EA001A5905 /* AppDelegate.swift */; }; 11 | 0E441D2F199939EA001A5905 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E441D2E199939EA001A5905 /* ViewController.swift */; }; 12 | 0E441D32199939EB001A5905 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E441D30199939EB001A5905 /* Main.storyboard */; }; 13 | 0E441D34199939EB001A5905 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E441D33199939EB001A5905 /* Images.xcassets */; }; 14 | 0E441D40199939EB001A5905 /* socketioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E441D3F199939EB001A5905 /* socketioTests.swift */; }; 15 | 0E441D50199944EB001A5905 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E441D4F199944EB001A5905 /* libicucore.dylib */; }; 16 | 0E441D52199944FA001A5905 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E441D51199944FA001A5905 /* CFNetwork.framework */; }; 17 | 0E441D5419994501001A5905 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E441D5319994501001A5905 /* Security.framework */; }; 18 | 0E441D5619994509001A5905 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E441D5519994509001A5905 /* Foundation.framework */; }; 19 | 0E441D7419994A99001A5905 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E441D7319994A99001A5905 /* SRWebSocket.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 0E441D3A199939EB001A5905 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 0E441D1F199939EA001A5905 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 0E441D26199939EA001A5905; 28 | remoteInfo = socketio; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 0E441D27199939EA001A5905 /* socketio.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = socketio.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 0E441D2B199939EA001A5905 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 0E441D2C199939EA001A5905 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36 | 0E441D2E199939EA001A5905 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 37 | 0E441D31199939EB001A5905 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 38 | 0E441D33199939EB001A5905 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 39 | 0E441D39199939EB001A5905 /* socketioTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = socketioTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 0E441D3E199939EB001A5905 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 0E441D3F199939EB001A5905 /* socketioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = socketioTests.swift; sourceTree = ""; }; 42 | 0E441D4F199944EB001A5905 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; 43 | 0E441D51199944FA001A5905 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 44 | 0E441D5319994501001A5905 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 45 | 0E441D5519994509001A5905 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 46 | 0E441D7119994A99001A5905 /* SocketRocket-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-Prefix.pch"; sourceTree = ""; }; 47 | 0E441D7219994A99001A5905 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = ""; }; 48 | 0E441D7319994A99001A5905 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = ""; }; 49 | 0E441D7519994AB4001A5905 /* socketio-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "socketio-Bridging-Header.h"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 0E441D24199939EA001A5905 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 0E441D5619994509001A5905 /* Foundation.framework in Frameworks */, 58 | 0E441D5419994501001A5905 /* Security.framework in Frameworks */, 59 | 0E441D52199944FA001A5905 /* CFNetwork.framework in Frameworks */, 60 | 0E441D50199944EB001A5905 /* libicucore.dylib in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | 0E441D36199939EB001A5905 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 0E441D1E199939EA001A5905 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 0E441D5519994509001A5905 /* Foundation.framework */, 78 | 0E441D5319994501001A5905 /* Security.framework */, 79 | 0E441D51199944FA001A5905 /* CFNetwork.framework */, 80 | 0E441D4F199944EB001A5905 /* libicucore.dylib */, 81 | 0E441D29199939EA001A5905 /* socketio */, 82 | 0E441D3C199939EB001A5905 /* socketioTests */, 83 | 0E441D28199939EA001A5905 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 0E441D28199939EA001A5905 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 0E441D27199939EA001A5905 /* socketio.app */, 91 | 0E441D39199939EB001A5905 /* socketioTests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | 0E441D29199939EA001A5905 /* socketio */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 0E441D7019994A99001A5905 /* SocketRocket */, 100 | 0E441D2C199939EA001A5905 /* AppDelegate.swift */, 101 | 0E441D2E199939EA001A5905 /* ViewController.swift */, 102 | 0E441D30199939EB001A5905 /* Main.storyboard */, 103 | 0E441D33199939EB001A5905 /* Images.xcassets */, 104 | 0E441D2A199939EA001A5905 /* Supporting Files */, 105 | 0E441D7519994AB4001A5905 /* socketio-Bridging-Header.h */, 106 | ); 107 | path = socketio; 108 | sourceTree = ""; 109 | }; 110 | 0E441D2A199939EA001A5905 /* Supporting Files */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 0E441D2B199939EA001A5905 /* Info.plist */, 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | 0E441D3C199939EB001A5905 /* socketioTests */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 0E441D3F199939EB001A5905 /* socketioTests.swift */, 122 | 0E441D3D199939EB001A5905 /* Supporting Files */, 123 | ); 124 | path = socketioTests; 125 | sourceTree = ""; 126 | }; 127 | 0E441D3D199939EB001A5905 /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 0E441D3E199939EB001A5905 /* Info.plist */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 0E441D7019994A99001A5905 /* SocketRocket */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 0E441D7119994A99001A5905 /* SocketRocket-Prefix.pch */, 139 | 0E441D7219994A99001A5905 /* SRWebSocket.h */, 140 | 0E441D7319994A99001A5905 /* SRWebSocket.m */, 141 | ); 142 | path = SocketRocket; 143 | sourceTree = ""; 144 | }; 145 | /* End PBXGroup section */ 146 | 147 | /* Begin PBXNativeTarget section */ 148 | 0E441D26199939EA001A5905 /* socketio */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 0E441D43199939EB001A5905 /* Build configuration list for PBXNativeTarget "socketio" */; 151 | buildPhases = ( 152 | 0E441D23199939EA001A5905 /* Sources */, 153 | 0E441D24199939EA001A5905 /* Frameworks */, 154 | 0E441D25199939EA001A5905 /* Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = socketio; 161 | productName = socketio; 162 | productReference = 0E441D27199939EA001A5905 /* socketio.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | 0E441D38199939EB001A5905 /* socketioTests */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 0E441D46199939EB001A5905 /* Build configuration list for PBXNativeTarget "socketioTests" */; 168 | buildPhases = ( 169 | 0E441D35199939EB001A5905 /* Sources */, 170 | 0E441D36199939EB001A5905 /* Frameworks */, 171 | 0E441D37199939EB001A5905 /* Resources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | 0E441D3B199939EB001A5905 /* PBXTargetDependency */, 177 | ); 178 | name = socketioTests; 179 | productName = socketioTests; 180 | productReference = 0E441D39199939EB001A5905 /* socketioTests.xctest */; 181 | productType = "com.apple.product-type.bundle.unit-test"; 182 | }; 183 | /* End PBXNativeTarget section */ 184 | 185 | /* Begin PBXProject section */ 186 | 0E441D1F199939EA001A5905 /* Project object */ = { 187 | isa = PBXProject; 188 | attributes = { 189 | LastUpgradeCheck = 0600; 190 | ORGANIZATIONNAME = "Teehan + Lax"; 191 | TargetAttributes = { 192 | 0E441D26199939EA001A5905 = { 193 | CreatedOnToolsVersion = 6.0; 194 | }; 195 | 0E441D38199939EB001A5905 = { 196 | CreatedOnToolsVersion = 6.0; 197 | TestTargetID = 0E441D26199939EA001A5905; 198 | }; 199 | }; 200 | }; 201 | buildConfigurationList = 0E441D22199939EA001A5905 /* Build configuration list for PBXProject "socketio" */; 202 | compatibilityVersion = "Xcode 3.2"; 203 | developmentRegion = English; 204 | hasScannedForEncodings = 0; 205 | knownRegions = ( 206 | en, 207 | Base, 208 | ); 209 | mainGroup = 0E441D1E199939EA001A5905; 210 | productRefGroup = 0E441D28199939EA001A5905 /* Products */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | 0E441D26199939EA001A5905 /* socketio */, 215 | 0E441D38199939EB001A5905 /* socketioTests */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | 0E441D25199939EA001A5905 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 0E441D32199939EB001A5905 /* Main.storyboard in Resources */, 226 | 0E441D34199939EB001A5905 /* Images.xcassets in Resources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | 0E441D37199939EB001A5905 /* Resources */ = { 231 | isa = PBXResourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXResourcesBuildPhase section */ 238 | 239 | /* Begin PBXSourcesBuildPhase section */ 240 | 0E441D23199939EA001A5905 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 0E441D7419994A99001A5905 /* SRWebSocket.m in Sources */, 245 | 0E441D2F199939EA001A5905 /* ViewController.swift in Sources */, 246 | 0E441D2D199939EA001A5905 /* AppDelegate.swift in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | 0E441D35199939EB001A5905 /* Sources */ = { 251 | isa = PBXSourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 0E441D40199939EB001A5905 /* socketioTests.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXSourcesBuildPhase section */ 259 | 260 | /* Begin PBXTargetDependency section */ 261 | 0E441D3B199939EB001A5905 /* PBXTargetDependency */ = { 262 | isa = PBXTargetDependency; 263 | target = 0E441D26199939EA001A5905 /* socketio */; 264 | targetProxy = 0E441D3A199939EB001A5905 /* PBXContainerItemProxy */; 265 | }; 266 | /* End PBXTargetDependency section */ 267 | 268 | /* Begin PBXVariantGroup section */ 269 | 0E441D30199939EB001A5905 /* Main.storyboard */ = { 270 | isa = PBXVariantGroup; 271 | children = ( 272 | 0E441D31199939EB001A5905 /* Base */, 273 | ); 274 | name = Main.storyboard; 275 | sourceTree = ""; 276 | }; 277 | /* End PBXVariantGroup section */ 278 | 279 | /* Begin XCBuildConfiguration section */ 280 | 0E441D41199939EB001A5905 /* Debug */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | ALWAYS_SEARCH_USER_PATHS = NO; 284 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 285 | CLANG_CXX_LIBRARY = "libc++"; 286 | CLANG_ENABLE_MODULES = YES; 287 | CLANG_ENABLE_OBJC_ARC = YES; 288 | CLANG_WARN_BOOL_CONVERSION = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INT_CONVERSION = YES; 294 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 295 | CLANG_WARN_UNREACHABLE_CODE = YES; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 298 | COPY_PHASE_STRIP = NO; 299 | ENABLE_STRICT_OBJC_MSGSEND = YES; 300 | GCC_C_LANGUAGE_STANDARD = gnu99; 301 | GCC_DYNAMIC_NO_PIC = NO; 302 | GCC_OPTIMIZATION_LEVEL = 0; 303 | GCC_PREPROCESSOR_DEFINITIONS = ( 304 | "DEBUG=1", 305 | "$(inherited)", 306 | ); 307 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 315 | MTL_ENABLE_DEBUG_INFO = YES; 316 | ONLY_ACTIVE_ARCH = YES; 317 | SDKROOT = iphoneos; 318 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 319 | TARGETED_DEVICE_FAMILY = "1,2"; 320 | }; 321 | name = Debug; 322 | }; 323 | 0E441D42199939EB001A5905 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = YES; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | 0E441D44199939EB001A5905 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 364 | INFOPLIST_FILE = socketio/Info.plist; 365 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 366 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SWIFT_OBJC_BRIDGING_HEADER = "socketio/socketio-Bridging-Header.h"; 369 | }; 370 | name = Debug; 371 | }; 372 | 0E441D45199939EB001A5905 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 377 | INFOPLIST_FILE = socketio/Info.plist; 378 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 379 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "socketio/socketio-Bridging-Header.h"; 382 | }; 383 | name = Release; 384 | }; 385 | 0E441D47199939EB001A5905 /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | BUNDLE_LOADER = "$(TEST_HOST)"; 389 | FRAMEWORK_SEARCH_PATHS = ( 390 | "$(SDKROOT)/Developer/Library/Frameworks", 391 | "$(inherited)", 392 | ); 393 | GCC_PREPROCESSOR_DEFINITIONS = ( 394 | "DEBUG=1", 395 | "$(inherited)", 396 | ); 397 | INFOPLIST_FILE = socketioTests/Info.plist; 398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/socketio.app/socketio"; 401 | }; 402 | name = Debug; 403 | }; 404 | 0E441D48199939EB001A5905 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | BUNDLE_LOADER = "$(TEST_HOST)"; 408 | FRAMEWORK_SEARCH_PATHS = ( 409 | "$(SDKROOT)/Developer/Library/Frameworks", 410 | "$(inherited)", 411 | ); 412 | INFOPLIST_FILE = socketioTests/Info.plist; 413 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/socketio.app/socketio"; 416 | }; 417 | name = Release; 418 | }; 419 | /* End XCBuildConfiguration section */ 420 | 421 | /* Begin XCConfigurationList section */ 422 | 0E441D22199939EA001A5905 /* Build configuration list for PBXProject "socketio" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | 0E441D41199939EB001A5905 /* Debug */, 426 | 0E441D42199939EB001A5905 /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | 0E441D43199939EB001A5905 /* Build configuration list for PBXNativeTarget "socketio" */ = { 432 | isa = XCConfigurationList; 433 | buildConfigurations = ( 434 | 0E441D44199939EB001A5905 /* Debug */, 435 | 0E441D45199939EB001A5905 /* Release */, 436 | ); 437 | defaultConfigurationIsVisible = 0; 438 | }; 439 | 0E441D46199939EB001A5905 /* Build configuration list for PBXNativeTarget "socketioTests" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | 0E441D47199939EB001A5905 /* Debug */, 443 | 0E441D48199939EB001A5905 /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | }; 447 | /* End XCConfigurationList section */ 448 | }; 449 | rootObject = 0E441D1F199939EA001A5905 /* Project object */; 450 | } 451 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/project.xcworkspace/xcshareddata/socketio.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 6886E185-E4FB-4481-B9FC-9FC103323402 9 | IDESourceControlProjectName 10 | socketio 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 011649BD1DEF42D991CEFA6990B3C2425220619B 14 | github.com:TeehanLax/Labs-OS.git 15 | 2B1ACBDB7DF1D4416E62AD9540FAC3F17C19CBF1 16 | github.com:TeehanLax/SwiftSocketIODemo.git 17 | 18 | IDESourceControlProjectPath 19 | socketio/socketio.xcodeproj 20 | IDESourceControlProjectRelativeInstallPathDictionary 21 | 22 | 011649BD1DEF42D991CEFA6990B3C2425220619B 23 | ../../../../Labs-OS 24 | 2B1ACBDB7DF1D4416E62AD9540FAC3F17C19CBF1 25 | ../../.. 26 | 27 | IDESourceControlProjectURL 28 | github.com:TeehanLax/SwiftSocketIODemo.git 29 | IDESourceControlProjectVersion 30 | 111 31 | IDESourceControlProjectWCCIdentifier 32 | 2B1ACBDB7DF1D4416E62AD9540FAC3F17C19CBF1 33 | IDESourceControlProjectWCConfigurations 34 | 35 | 36 | IDESourceControlRepositoryExtensionIdentifierKey 37 | public.vcs.git 38 | IDESourceControlWCCIdentifierKey 39 | 011649BD1DEF42D991CEFA6990B3C2425220619B 40 | IDESourceControlWCCName 41 | Labs-OS 42 | 43 | 44 | IDESourceControlRepositoryExtensionIdentifierKey 45 | public.vcs.git 46 | IDESourceControlWCCIdentifierKey 47 | 2B1ACBDB7DF1D4416E62AD9540FAC3F17C19CBF1 48 | IDESourceControlWCCName 49 | SwiftSocketIODemo 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/project.xcworkspace/xcuserdata/derekkinsman.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeehanLax/SwiftSocketIODemo/7248fd3a3449e9a83eef20326279025872ee517a/socketio/socketio.xcodeproj/project.xcworkspace/xcuserdata/derekkinsman.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/project.xcworkspace/xcuserdata/derekkinsman.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | SnapshotAutomaticallyBeforeSignificantChanges 16 | 17 | SnapshotLocationStyle 18 | Default 19 | 20 | 21 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/xcuserdata/derekkinsman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/xcuserdata/derekkinsman.xcuserdatad/xcschemes/socketio.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /socketio/socketio.xcodeproj/xcuserdata/derekkinsman.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | socketio.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 0E441D26199939EA001A5905 16 | 17 | primary 18 | 19 | 20 | 0E441D38199939EB001A5905 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /socketio/socketio/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // socketio 4 | // 5 | // Created by Derek J. Kinsman on 2014-08-11. 6 | // Copyright (c) 2014 Teehan + Lax. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool { 18 | 19 | let VC:ViewController = ViewController() 20 | self.window!.rootViewController = VC 21 | self.window!.backgroundColor = UIColor.whiteColor() 22 | 23 | return true 24 | } 25 | 26 | func applicationWillResignActive(application: UIApplication!) {} 27 | func applicationDidEnterBackground(application: UIApplication!) {} 28 | func applicationWillEnterForeground(application: UIApplication!) {} 29 | func applicationDidBecomeActive(application: UIApplication!) {} 30 | func applicationWillTerminate(application: UIApplication!) {} 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /socketio/socketio/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 | -------------------------------------------------------------------------------- /socketio/socketio/Images.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" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /socketio/socketio/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /socketio/socketio/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.teehanlax.labs.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /socketio/socketio/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 enum { 21 | SR_CONNECTING = 0, 22 | SR_OPEN = 1, 23 | SR_CLOSING = 2, 24 | SR_CLOSED = 3, 25 | } SRReadyState; 26 | 27 | typedef enum SRStatusCode : NSInteger { 28 | SRStatusCodeNormal = 1000, 29 | SRStatusCodeGoingAway = 1001, 30 | SRStatusCodeProtocolError = 1002, 31 | SRStatusCodeUnhandledType = 1003, 32 | // 1004 reserved. 33 | SRStatusNoStatusReceived = 1005, 34 | // 1004-1006 reserved. 35 | SRStatusCodeInvalidUTF8 = 1007, 36 | SRStatusCodePolicyViolated = 1008, 37 | SRStatusCodeMessageTooBig = 1009, 38 | } SRStatusCode; 39 | 40 | @class SRWebSocket; 41 | 42 | extern NSString *const SRWebSocketErrorDomain; 43 | 44 | #pragma mark - SRWebSocketDelegate 45 | 46 | @protocol SRWebSocketDelegate; 47 | 48 | #pragma mark - SRWebSocket 49 | 50 | @interface SRWebSocket : NSObject 51 | 52 | @property (nonatomic, assign) id delegate; 53 | 54 | @property (nonatomic, readonly) SRReadyState readyState; 55 | @property (nonatomic, readonly, retain) NSURL *url; 56 | 57 | // This returns the negotiated protocol. 58 | // It will be nil until after the handshake completes. 59 | @property (nonatomic, readonly, copy) NSString *protocol; 60 | 61 | // Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. 62 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 63 | - (id)initWithURLRequest:(NSURLRequest *)request; 64 | 65 | // Some helper constructors. 66 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 67 | - (id)initWithURL:(NSURL *)url; 68 | 69 | // Delegate queue will be dispatch_main_queue by default. 70 | // You cannot set both OperationQueue and dispatch_queue. 71 | - (void)setDelegateOperationQueue:(NSOperationQueue*) queue; 72 | - (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; 73 | 74 | // By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. 75 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 76 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 77 | 78 | // SRWebSockets are intended for one-time-use only. Open should be called once and only once. 79 | - (void)open; 80 | 81 | - (void)close; 82 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 83 | 84 | // Send a UTF8 String or Data. 85 | - (void)send:(id)data; 86 | 87 | @end 88 | 89 | #pragma mark - SRWebSocketDelegate 90 | 91 | @protocol SRWebSocketDelegate 92 | 93 | // message will either be an NSString if the server is using text 94 | // or NSData if the server is using binary. 95 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 96 | 97 | @optional 98 | 99 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 100 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 101 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 102 | 103 | @end 104 | 105 | #pragma mark - NSURLRequest (CertificateAdditions) 106 | 107 | @interface NSURLRequest (CertificateAdditions) 108 | 109 | @property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; 110 | 111 | @end 112 | 113 | #pragma mark - NSMutableURLRequest (CertificateAdditions) 114 | 115 | @interface NSMutableURLRequest (CertificateAdditions) 116 | 117 | @property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; 118 | 119 | @end 120 | 121 | #pragma mark - NSRunLoop (SRWebSocket) 122 | 123 | @interface NSRunLoop (SRWebSocket) 124 | 125 | + (NSRunLoop *)SR_networkRunLoop; 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /socketio/socketio/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 | return [[NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH] base64Encoding]; 115 | } 116 | 117 | @implementation NSData (SRWebSocket) 118 | 119 | - (NSString *)stringBySHA1ThenBase64Encoding; 120 | { 121 | return newSHA1String(self.bytes, self.length); 122 | } 123 | 124 | @end 125 | 126 | 127 | @implementation NSString (SRWebSocket) 128 | 129 | - (NSString *)stringBySHA1ThenBase64Encoding; 130 | { 131 | return newSHA1String(self.UTF8String, self.length); 132 | } 133 | 134 | @end 135 | 136 | NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; 137 | 138 | // Returns number of bytes consumed. Returning 0 means you didn't match. 139 | // Sends bytes to callback handler; 140 | typedef size_t (^stream_scanner)(NSData *collected_data); 141 | 142 | typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); 143 | 144 | @interface SRIOConsumer : NSObject { 145 | stream_scanner _scanner; 146 | data_callback _handler; 147 | size_t _bytesNeeded; 148 | BOOL _readToCurrentFrame; 149 | BOOL _unmaskBytes; 150 | } 151 | @property (nonatomic, copy, readonly) stream_scanner consumer; 152 | @property (nonatomic, copy, readonly) data_callback handler; 153 | @property (nonatomic, assign) size_t bytesNeeded; 154 | @property (nonatomic, assign, readonly) BOOL readToCurrentFrame; 155 | @property (nonatomic, assign, readonly) BOOL unmaskBytes; 156 | 157 | @end 158 | 159 | // This class is not thread-safe, and is expected to always be run on the same queue. 160 | @interface SRIOConsumerPool : NSObject 161 | 162 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 163 | 164 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 165 | - (void)returnConsumer:(SRIOConsumer *)consumer; 166 | 167 | @end 168 | 169 | @interface SRWebSocket () 170 | 171 | - (void)_writeData:(NSData *)data; 172 | - (void)_closeWithProtocolError:(NSString *)message; 173 | - (void)_failWithError:(NSError *)error; 174 | 175 | - (void)_disconnect; 176 | 177 | - (void)_readFrameNew; 178 | - (void)_readFrameContinue; 179 | 180 | - (void)_pumpScanner; 181 | 182 | - (void)_pumpWriting; 183 | 184 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; 185 | - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 186 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; 187 | - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; 188 | - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; 189 | 190 | - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; 191 | 192 | - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; 193 | - (void)_SR_commonInit; 194 | 195 | - (void)_initializeStreams; 196 | - (void)_connect; 197 | 198 | @property (nonatomic) SRReadyState readyState; 199 | 200 | @property (nonatomic) NSOperationQueue *delegateOperationQueue; 201 | @property (nonatomic) dispatch_queue_t delegateDispatchQueue; 202 | 203 | @end 204 | 205 | 206 | @implementation SRWebSocket { 207 | NSInteger _webSocketVersion; 208 | 209 | NSOperationQueue *_delegateOperationQueue; 210 | dispatch_queue_t _delegateDispatchQueue; 211 | 212 | dispatch_queue_t _workQueue; 213 | NSMutableArray *_consumers; 214 | 215 | NSInputStream *_inputStream; 216 | NSOutputStream *_outputStream; 217 | 218 | NSMutableData *_readBuffer; 219 | NSUInteger _readBufferOffset; 220 | 221 | NSMutableData *_outputBuffer; 222 | NSUInteger _outputBufferOffset; 223 | 224 | uint8_t _currentFrameOpcode; 225 | size_t _currentFrameCount; 226 | size_t _readOpCount; 227 | uint32_t _currentStringScanPosition; 228 | NSMutableData *_currentFrameData; 229 | 230 | NSString *_closeReason; 231 | 232 | NSString *_secKey; 233 | 234 | BOOL _pinnedCertFound; 235 | 236 | uint8_t _currentReadMaskKey[4]; 237 | size_t _currentReadMaskOffset; 238 | 239 | BOOL _consumerStopped; 240 | 241 | BOOL _closeWhenFinishedWriting; 242 | BOOL _failed; 243 | 244 | BOOL _secure; 245 | NSURLRequest *_urlRequest; 246 | 247 | CFHTTPMessageRef _receivedHTTPHeaders; 248 | 249 | BOOL _sentClose; 250 | BOOL _didFail; 251 | int _closeCode; 252 | 253 | BOOL _isPumping; 254 | 255 | NSMutableSet *_scheduledRunloops; 256 | 257 | // We use this to retain ourselves. 258 | __strong SRWebSocket *_selfRetain; 259 | 260 | NSArray *_requestedProtocols; 261 | SRIOConsumerPool *_consumerPool; 262 | } 263 | 264 | @synthesize delegate = _delegate; 265 | @synthesize url = _url; 266 | @synthesize readyState = _readyState; 267 | @synthesize protocol = _protocol; 268 | 269 | static __strong NSData *CRLFCRLF; 270 | 271 | + (void)initialize; 272 | { 273 | CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 274 | } 275 | 276 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 277 | { 278 | self = [super init]; 279 | if (self) { 280 | assert(request.URL); 281 | _url = request.URL; 282 | _urlRequest = request; 283 | 284 | _requestedProtocols = [protocols copy]; 285 | 286 | [self _SR_commonInit]; 287 | } 288 | 289 | return self; 290 | } 291 | 292 | - (id)initWithURLRequest:(NSURLRequest *)request; 293 | { 294 | return [self initWithURLRequest:request protocols:nil]; 295 | } 296 | 297 | - (id)initWithURL:(NSURL *)url; 298 | { 299 | return [self initWithURL:url protocols:nil]; 300 | } 301 | 302 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 303 | { 304 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 305 | return [self initWithURLRequest:request protocols:protocols]; 306 | } 307 | 308 | - (void)_SR_commonInit; 309 | { 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 | sr_dispatch_release(_workQueue); 360 | _workQueue = NULL; 361 | 362 | if (_receivedHTTPHeaders) { 363 | CFRelease(_receivedHTTPHeaders); 364 | _receivedHTTPHeaders = NULL; 365 | } 366 | 367 | if (_delegateDispatchQueue) { 368 | sr_dispatch_release(_delegateDispatchQueue); 369 | _delegateDispatchQueue = NULL; 370 | } 371 | } 372 | 373 | #ifndef NDEBUG 374 | 375 | - (void)setReadyState:(SRReadyState)aReadyState; 376 | { 377 | [self willChangeValueForKey:@"readyState"]; 378 | assert(aReadyState > _readyState); 379 | _readyState = aReadyState; 380 | [self didChangeValueForKey:@"readyState"]; 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 | [self _connect]; 393 | } 394 | 395 | // Calls block on delegate queue 396 | - (void)_performDelegateBlock:(dispatch_block_t)block; 397 | { 398 | if (_delegateOperationQueue) { 399 | [_delegateOperationQueue addOperationWithBlock:block]; 400 | } else { 401 | assert(_delegateDispatchQueue); 402 | dispatch_async(_delegateDispatchQueue, block); 403 | } 404 | } 405 | 406 | - (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; 407 | { 408 | if (queue) { 409 | sr_dispatch_retain(queue); 410 | } 411 | 412 | if (_delegateDispatchQueue) { 413 | sr_dispatch_release(_delegateDispatchQueue); 414 | } 415 | 416 | _delegateDispatchQueue = queue; 417 | } 418 | 419 | - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; 420 | { 421 | NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); 422 | 423 | if (acceptHeader == nil) { 424 | return NO; 425 | } 426 | 427 | NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; 428 | NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; 429 | 430 | return [acceptHeader isEqualToString:expectedAccept]; 431 | } 432 | 433 | - (void)_HTTPHeadersDidFinish; 434 | { 435 | NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); 436 | 437 | if (responseCode >= 400) { 438 | SRFastLog(@"Request failed with response code %d", responseCode); 439 | [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2132 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode] forKey:NSLocalizedDescriptionKey]]]; 440 | return; 441 | 442 | } 443 | 444 | if(![self _checkHandshake:_receivedHTTPHeaders]) { 445 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; 446 | return; 447 | } 448 | 449 | NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); 450 | if (negotiatedProtocol) { 451 | // Make sure we requested the protocol 452 | if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { 453 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; 454 | return; 455 | } 456 | 457 | _protocol = negotiatedProtocol; 458 | } 459 | 460 | self.readyState = SR_OPEN; 461 | 462 | if (!_didFail) { 463 | [self _readFrameNew]; 464 | } 465 | 466 | [self _performDelegateBlock:^{ 467 | if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { 468 | [self.delegate webSocketDidOpen:self]; 469 | }; 470 | }]; 471 | } 472 | 473 | 474 | - (void)_readHTTPHeader; 475 | { 476 | if (_receivedHTTPHeaders == NULL) { 477 | _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); 478 | } 479 | 480 | [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { 481 | CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); 482 | 483 | if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { 484 | SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); 485 | [self _HTTPHeadersDidFinish]; 486 | } else { 487 | [self _readHTTPHeader]; 488 | } 489 | }]; 490 | } 491 | 492 | - (void)didConnect 493 | { 494 | SRFastLog(@"Connected"); 495 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); 496 | 497 | // Set host first so it defaults 498 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); 499 | 500 | NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; 501 | SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); 502 | _secKey = keyBytes.base64Encoding; 503 | assert([_secKey length] == 24); 504 | 505 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); 506 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); 507 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); 508 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); 509 | 510 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); 511 | 512 | if (_requestedProtocols) { 513 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); 514 | } 515 | 516 | [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 517 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); 518 | }]; 519 | 520 | NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); 521 | 522 | CFRelease(request); 523 | 524 | [self _writeData:message]; 525 | [self _readHTTPHeader]; 526 | } 527 | 528 | - (void)_initializeStreams; 529 | { 530 | assert(_url.port.unsignedIntValue <= UINT32_MAX); 531 | uint32_t port = _url.port.unsignedIntValue; 532 | if (port == 0) { 533 | if (!_secure) { 534 | port = 80; 535 | } else { 536 | port = 443; 537 | } 538 | } 539 | NSString *host = _url.host; 540 | 541 | CFReadStreamRef readStream = NULL; 542 | CFWriteStreamRef writeStream = NULL; 543 | 544 | CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); 545 | 546 | _outputStream = CFBridgingRelease(writeStream); 547 | _inputStream = CFBridgingRelease(readStream); 548 | 549 | 550 | if (_secure) { 551 | NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; 552 | 553 | [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; 554 | 555 | // If we're using pinned certs, don't validate the certificate chain 556 | if ([_urlRequest SR_SSLPinnedCertificates].count) { 557 | [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 558 | } 559 | 560 | #if DEBUG 561 | [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 562 | NSLog(@"SocketRocket: In debug mode. Allowing connection to any root cert"); 563 | #endif 564 | 565 | [_outputStream setProperty:SSLOptions 566 | forKey:(__bridge id)kCFStreamPropertySSLSettings]; 567 | } 568 | 569 | _inputStream.delegate = self; 570 | _outputStream.delegate = self; 571 | } 572 | 573 | - (void)_connect; 574 | { 575 | if (!_scheduledRunloops.count) { 576 | [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; 577 | } 578 | 579 | 580 | [_outputStream open]; 581 | [_inputStream open]; 582 | } 583 | 584 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 585 | { 586 | [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; 587 | [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; 588 | 589 | [_scheduledRunloops addObject:@[aRunLoop, mode]]; 590 | } 591 | 592 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 593 | { 594 | [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; 595 | [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; 596 | 597 | [_scheduledRunloops removeObject:@[aRunLoop, mode]]; 598 | } 599 | 600 | - (void)close; 601 | { 602 | [self closeWithCode:SRStatusCodeNormal reason:nil]; 603 | } 604 | 605 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 606 | { 607 | assert(code); 608 | dispatch_async(_workQueue, ^{ 609 | if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { 610 | return; 611 | } 612 | 613 | BOOL wasConnecting = self.readyState == SR_CONNECTING; 614 | 615 | self.readyState = SR_CLOSING; 616 | 617 | SRFastLog(@"Closing with code %d reason %@", code, reason); 618 | 619 | if (wasConnecting) { 620 | [self _disconnect]; 621 | return; 622 | } 623 | 624 | size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 625 | NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; 626 | NSData *payload = mutablePayload; 627 | 628 | ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); 629 | 630 | if (reason) { 631 | NSRange remainingRange = {0}; 632 | 633 | NSUInteger usedLength = 0; 634 | 635 | 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]; 636 | 637 | assert(success); 638 | assert(remainingRange.length == 0); 639 | 640 | if (usedLength != maxMsgSize) { 641 | payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; 642 | } 643 | } 644 | 645 | 646 | [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; 647 | }); 648 | } 649 | 650 | - (void)_closeWithProtocolError:(NSString *)message; 651 | { 652 | // Need to shunt this on the _callbackQueue first to see if they received any messages 653 | [self _performDelegateBlock:^{ 654 | [self closeWithCode:SRStatusCodeProtocolError reason:message]; 655 | dispatch_async(_workQueue, ^{ 656 | [self _disconnect]; 657 | }); 658 | }]; 659 | } 660 | 661 | - (void)_failWithError:(NSError *)error; 662 | { 663 | dispatch_async(_workQueue, ^{ 664 | if (self.readyState != SR_CLOSED) { 665 | _failed = YES; 666 | [self _performDelegateBlock:^{ 667 | if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { 668 | [self.delegate webSocket:self didFailWithError:error]; 669 | } 670 | }]; 671 | 672 | self.readyState = SR_CLOSED; 673 | _selfRetain = nil; 674 | 675 | SRFastLog(@"Failing with error %@", error.localizedDescription); 676 | 677 | [self _disconnect]; 678 | } 679 | }); 680 | } 681 | 682 | - (void)_writeData:(NSData *)data; 683 | { 684 | [self assertOnWorkQueue]; 685 | 686 | if (_closeWhenFinishedWriting) { 687 | return; 688 | } 689 | [_outputBuffer appendData:data]; 690 | [self _pumpWriting]; 691 | } 692 | - (void)send:(id)data; 693 | { 694 | NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); 695 | // TODO: maybe not copy this for performance 696 | data = [data copy]; 697 | dispatch_async(_workQueue, ^{ 698 | if ([data isKindOfClass:[NSString class]]) { 699 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; 700 | } else if ([data isKindOfClass:[NSData class]]) { 701 | [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; 702 | } else if (data == nil) { 703 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; 704 | } else { 705 | assert(NO); 706 | } 707 | }); 708 | } 709 | 710 | - (void)handlePing:(NSData *)pingData; 711 | { 712 | // Need to pingpong this off _callbackQueue first to make sure messages happen in order 713 | [self _performDelegateBlock:^{ 714 | dispatch_async(_workQueue, ^{ 715 | [self _sendFrameWithOpcode:SROpCodePong data:pingData]; 716 | }); 717 | }]; 718 | } 719 | 720 | - (void)handlePong; 721 | { 722 | // NOOP 723 | } 724 | 725 | - (void)_handleMessage:(id)message 726 | { 727 | SRFastLog(@"Received message"); 728 | [self _performDelegateBlock:^{ 729 | [self.delegate webSocket:self didReceiveMessage:message]; 730 | }]; 731 | } 732 | 733 | 734 | static inline BOOL closeCodeIsValid(int closeCode) { 735 | if (closeCode < 1000) { 736 | return NO; 737 | } 738 | 739 | if (closeCode >= 1000 && closeCode <= 1011) { 740 | if (closeCode == 1004 || 741 | closeCode == 1005 || 742 | closeCode == 1006) { 743 | return NO; 744 | } 745 | return YES; 746 | } 747 | 748 | if (closeCode >= 3000 && closeCode <= 3999) { 749 | return YES; 750 | } 751 | 752 | if (closeCode >= 4000 && closeCode <= 4999) { 753 | return YES; 754 | } 755 | 756 | return NO; 757 | } 758 | 759 | // Note from RFC: 760 | // 761 | // If there is a body, the first two 762 | // bytes of the body MUST be a 2-byte unsigned integer (in network byte 763 | // order) representing a status code with value /code/ defined in 764 | // Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 765 | // encoded data with value /reason/, the interpretation of which is not 766 | // defined by this specification. 767 | 768 | - (void)handleCloseWithData:(NSData *)data; 769 | { 770 | size_t dataSize = data.length; 771 | __block uint16_t closeCode = 0; 772 | 773 | SRFastLog(@"Received close frame"); 774 | 775 | if (dataSize == 1) { 776 | // TODO handle error 777 | [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; 778 | return; 779 | } else if (dataSize >= 2) { 780 | [data getBytes:&closeCode length:sizeof(closeCode)]; 781 | _closeCode = EndianU16_BtoN(closeCode); 782 | if (!closeCodeIsValid(_closeCode)) { 783 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; 784 | return; 785 | } 786 | if (dataSize > 2) { 787 | _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; 788 | if (!_closeReason) { 789 | [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; 790 | return; 791 | } 792 | } 793 | } else { 794 | _closeCode = SRStatusNoStatusReceived; 795 | } 796 | 797 | [self assertOnWorkQueue]; 798 | 799 | if (self.readyState == SR_OPEN) { 800 | [self closeWithCode:1000 reason:nil]; 801 | } 802 | dispatch_async(_workQueue, ^{ 803 | [self _disconnect]; 804 | }); 805 | } 806 | 807 | - (void)_disconnect; 808 | { 809 | [self assertOnWorkQueue]; 810 | SRFastLog(@"Trying to disconnect"); 811 | _closeWhenFinishedWriting = YES; 812 | [self _pumpWriting]; 813 | } 814 | 815 | - (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; 816 | { 817 | // Check that the current data is valid UTF8 818 | 819 | BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); 820 | if (!isControlFrame) { 821 | [self _readFrameNew]; 822 | } else { 823 | dispatch_async(_workQueue, ^{ 824 | [self _readFrameContinue]; 825 | }); 826 | } 827 | 828 | switch (opcode) { 829 | case SROpCodeTextFrame: { 830 | NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; 831 | if (str == nil && frameData) { 832 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 833 | dispatch_async(_workQueue, ^{ 834 | [self _disconnect]; 835 | }); 836 | 837 | return; 838 | } 839 | [self _handleMessage:str]; 840 | break; 841 | } 842 | case SROpCodeBinaryFrame: 843 | [self _handleMessage:[frameData copy]]; 844 | break; 845 | case SROpCodeConnectionClose: 846 | [self handleCloseWithData:frameData]; 847 | break; 848 | case SROpCodePing: 849 | [self handlePing:frameData]; 850 | break; 851 | case SROpCodePong: 852 | [self handlePong]; 853 | break; 854 | default: 855 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; 856 | // TODO: Handle invalid opcode 857 | break; 858 | } 859 | } 860 | 861 | - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; 862 | { 863 | assert(frame_header.opcode != 0); 864 | 865 | if (self.readyState != SR_OPEN) { 866 | return; 867 | } 868 | 869 | 870 | BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); 871 | 872 | if (isControlFrame && !frame_header.fin) { 873 | [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; 874 | return; 875 | } 876 | 877 | if (isControlFrame && frame_header.payload_length >= 126) { 878 | [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; 879 | return; 880 | } 881 | 882 | if (!isControlFrame) { 883 | _currentFrameOpcode = frame_header.opcode; 884 | _currentFrameCount += 1; 885 | } 886 | 887 | if (frame_header.payload_length == 0) { 888 | if (isControlFrame) { 889 | [self _handleFrameWithData:curData opCode:frame_header.opcode]; 890 | } else { 891 | if (frame_header.fin) { 892 | [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; 893 | } else { 894 | // TODO add assert that opcode is not a control; 895 | [self _readFrameContinue]; 896 | } 897 | } 898 | } else { 899 | assert(frame_header.payload_length <= SIZE_T_MAX); 900 | [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { 901 | if (isControlFrame) { 902 | [self _handleFrameWithData:newData opCode:frame_header.opcode]; 903 | } else { 904 | if (frame_header.fin) { 905 | [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; 906 | } else { 907 | // TODO add assert that opcode is not a control; 908 | [self _readFrameContinue]; 909 | } 910 | 911 | } 912 | } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; 913 | } 914 | } 915 | 916 | /* From RFC: 917 | 918 | 0 1 2 3 919 | 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 920 | +-+-+-+-+-------+-+-------------+-------------------------------+ 921 | |F|R|R|R| opcode|M| Payload len | Extended payload length | 922 | |I|S|S|S| (4) |A| (7) | (16/64) | 923 | |N|V|V|V| |S| | (if payload len==126/127) | 924 | | |1|2|3| |K| | | 925 | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 926 | | Extended payload length continued, if payload len == 127 | 927 | + - - - - - - - - - - - - - - - +-------------------------------+ 928 | | |Masking-key, if MASK set to 1 | 929 | +-------------------------------+-------------------------------+ 930 | | Masking-key (continued) | Payload Data | 931 | +-------------------------------- - - - - - - - - - - - - - - - + 932 | : Payload Data continued ... : 933 | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 934 | | Payload Data continued ... | 935 | +---------------------------------------------------------------+ 936 | */ 937 | 938 | static const uint8_t SRFinMask = 0x80; 939 | static const uint8_t SROpCodeMask = 0x0F; 940 | static const uint8_t SRRsvMask = 0x70; 941 | static const uint8_t SRMaskMask = 0x80; 942 | static const uint8_t SRPayloadLenMask = 0x7F; 943 | 944 | 945 | - (void)_readFrameContinue; 946 | { 947 | assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); 948 | 949 | [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { 950 | __block frame_header header = {0}; 951 | 952 | const uint8_t *headerBuffer = data.bytes; 953 | assert(data.length >= 2); 954 | 955 | if (headerBuffer[0] & SRRsvMask) { 956 | [self _closeWithProtocolError:@"Server used RSV bits"]; 957 | return; 958 | } 959 | 960 | uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); 961 | 962 | BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); 963 | 964 | if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { 965 | [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; 966 | return; 967 | } 968 | 969 | if (receivedOpcode == 0 && self->_currentFrameCount == 0) { 970 | [self _closeWithProtocolError:@"cannot continue a message"]; 971 | return; 972 | } 973 | 974 | header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; 975 | 976 | header.fin = !!(SRFinMask & headerBuffer[0]); 977 | 978 | 979 | header.masked = !!(SRMaskMask & headerBuffer[1]); 980 | header.payload_length = SRPayloadLenMask & headerBuffer[1]; 981 | 982 | headerBuffer = NULL; 983 | 984 | if (header.masked) { 985 | [self _closeWithProtocolError:@"Client must receive unmasked data"]; 986 | } 987 | 988 | size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; 989 | 990 | if (header.payload_length == 126) { 991 | extra_bytes_needed += sizeof(uint16_t); 992 | } else if (header.payload_length == 127) { 993 | extra_bytes_needed += sizeof(uint64_t); 994 | } 995 | 996 | if (extra_bytes_needed == 0) { 997 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 998 | } else { 999 | [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { 1000 | size_t mapped_size = data.length; 1001 | const void *mapped_buffer = data.bytes; 1002 | size_t offset = 0; 1003 | 1004 | if (header.payload_length == 126) { 1005 | assert(mapped_size >= sizeof(uint16_t)); 1006 | uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); 1007 | header.payload_length = newLen; 1008 | offset += sizeof(uint16_t); 1009 | } else if (header.payload_length == 127) { 1010 | assert(mapped_size >= sizeof(uint64_t)); 1011 | header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); 1012 | offset += sizeof(uint64_t); 1013 | } else { 1014 | assert(header.payload_length < 126 && header.payload_length >= 0); 1015 | } 1016 | 1017 | 1018 | if (header.masked) { 1019 | assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); 1020 | memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); 1021 | } 1022 | 1023 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1024 | } readToCurrentFrame:NO unmaskBytes:NO]; 1025 | } 1026 | } readToCurrentFrame:NO unmaskBytes:NO]; 1027 | } 1028 | 1029 | - (void)_readFrameNew; 1030 | { 1031 | dispatch_async(_workQueue, ^{ 1032 | [_currentFrameData setLength:0]; 1033 | 1034 | _currentFrameOpcode = 0; 1035 | _currentFrameCount = 0; 1036 | _readOpCount = 0; 1037 | _currentStringScanPosition = 0; 1038 | 1039 | [self _readFrameContinue]; 1040 | }); 1041 | } 1042 | 1043 | - (void)_pumpWriting; 1044 | { 1045 | [self assertOnWorkQueue]; 1046 | 1047 | NSUInteger dataLength = _outputBuffer.length; 1048 | if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { 1049 | NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; 1050 | if (bytesWritten == -1) { 1051 | [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; 1052 | return; 1053 | } 1054 | 1055 | _outputBufferOffset += bytesWritten; 1056 | 1057 | if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { 1058 | _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; 1059 | _outputBufferOffset = 0; 1060 | } 1061 | } 1062 | 1063 | if (_closeWhenFinishedWriting && 1064 | _outputBuffer.length - _outputBufferOffset == 0 && 1065 | (_inputStream.streamStatus != NSStreamStatusNotOpen && 1066 | _inputStream.streamStatus != NSStreamStatusClosed) && 1067 | !_sentClose) { 1068 | _sentClose = YES; 1069 | 1070 | [_outputStream close]; 1071 | [_inputStream close]; 1072 | 1073 | 1074 | for (NSArray *runLoop in [_scheduledRunloops copy]) { 1075 | [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; 1076 | } 1077 | 1078 | if (!_failed) { 1079 | [self _performDelegateBlock:^{ 1080 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1081 | [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; 1082 | } 1083 | }]; 1084 | } 1085 | 1086 | _selfRetain = nil; 1087 | } 1088 | } 1089 | 1090 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; 1091 | { 1092 | [self assertOnWorkQueue]; 1093 | [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; 1094 | } 1095 | 1096 | - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1097 | { 1098 | [self assertOnWorkQueue]; 1099 | assert(dataLength); 1100 | 1101 | [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; 1102 | [self _pumpScanner]; 1103 | } 1104 | 1105 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; 1106 | { 1107 | [self assertOnWorkQueue]; 1108 | [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; 1109 | [self _pumpScanner]; 1110 | } 1111 | 1112 | 1113 | static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; 1114 | 1115 | - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; 1116 | { 1117 | [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; 1118 | } 1119 | 1120 | - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; 1121 | { 1122 | // TODO optimize so this can continue from where we last searched 1123 | stream_scanner consumer = ^size_t(NSData *data) { 1124 | __block size_t found_size = 0; 1125 | __block size_t match_count = 0; 1126 | 1127 | size_t size = data.length; 1128 | const unsigned char *buffer = data.bytes; 1129 | for (size_t i = 0; i < size; i++ ) { 1130 | if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { 1131 | match_count += 1; 1132 | if (match_count == length) { 1133 | found_size = i + 1; 1134 | break; 1135 | } 1136 | } else { 1137 | match_count = 0; 1138 | } 1139 | } 1140 | return found_size; 1141 | }; 1142 | [self _addConsumerWithScanner:consumer callback:dataHandler]; 1143 | } 1144 | 1145 | 1146 | // Returns true if did work 1147 | - (BOOL)_innerPumpScanner { 1148 | 1149 | BOOL didWork = NO; 1150 | 1151 | if (self.readyState >= SR_CLOSING) { 1152 | return didWork; 1153 | } 1154 | 1155 | if (!_consumers.count) { 1156 | return didWork; 1157 | } 1158 | 1159 | size_t curSize = _readBuffer.length - _readBufferOffset; 1160 | if (!curSize) { 1161 | return didWork; 1162 | } 1163 | 1164 | SRIOConsumer *consumer = [_consumers objectAtIndex:0]; 1165 | 1166 | size_t bytesNeeded = consumer.bytesNeeded; 1167 | 1168 | size_t foundSize = 0; 1169 | if (consumer.consumer) { 1170 | NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; 1171 | foundSize = consumer.consumer(tempView); 1172 | } else { 1173 | assert(consumer.bytesNeeded); 1174 | if (curSize >= bytesNeeded) { 1175 | foundSize = bytesNeeded; 1176 | } else if (consumer.readToCurrentFrame) { 1177 | foundSize = curSize; 1178 | } 1179 | } 1180 | 1181 | NSData *slice = nil; 1182 | if (consumer.readToCurrentFrame || foundSize) { 1183 | NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); 1184 | slice = [_readBuffer subdataWithRange:sliceRange]; 1185 | 1186 | _readBufferOffset += foundSize; 1187 | 1188 | if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { 1189 | _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; 1190 | } 1191 | 1192 | if (consumer.unmaskBytes) { 1193 | NSMutableData *mutableSlice = [slice mutableCopy]; 1194 | 1195 | NSUInteger len = mutableSlice.length; 1196 | uint8_t *bytes = mutableSlice.mutableBytes; 1197 | 1198 | for (NSUInteger i = 0; i < len; i++) { 1199 | bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; 1200 | _currentReadMaskOffset += 1; 1201 | } 1202 | 1203 | slice = mutableSlice; 1204 | } 1205 | 1206 | if (consumer.readToCurrentFrame) { 1207 | [_currentFrameData appendData:slice]; 1208 | 1209 | _readOpCount += 1; 1210 | 1211 | if (_currentFrameOpcode == SROpCodeTextFrame) { 1212 | // Validate UTF8 stuff. 1213 | size_t currentDataSize = _currentFrameData.length; 1214 | if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { 1215 | // TODO: Optimize the crap out of this. Don't really have to copy all the data each time 1216 | 1217 | size_t scanSize = currentDataSize - _currentStringScanPosition; 1218 | 1219 | NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; 1220 | int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); 1221 | 1222 | if (valid_utf8_size == -1) { 1223 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 1224 | dispatch_async(_workQueue, ^{ 1225 | [self _disconnect]; 1226 | }); 1227 | return didWork; 1228 | } else { 1229 | _currentStringScanPosition += valid_utf8_size; 1230 | } 1231 | } 1232 | 1233 | } 1234 | 1235 | consumer.bytesNeeded -= foundSize; 1236 | 1237 | if (consumer.bytesNeeded == 0) { 1238 | [_consumers removeObjectAtIndex:0]; 1239 | consumer.handler(self, nil); 1240 | [_consumerPool returnConsumer:consumer]; 1241 | didWork = YES; 1242 | } 1243 | } else if (foundSize) { 1244 | [_consumers removeObjectAtIndex:0]; 1245 | consumer.handler(self, slice); 1246 | [_consumerPool returnConsumer:consumer]; 1247 | didWork = YES; 1248 | } 1249 | } 1250 | return didWork; 1251 | } 1252 | 1253 | -(void)_pumpScanner; 1254 | { 1255 | [self assertOnWorkQueue]; 1256 | 1257 | if (!_isPumping) { 1258 | _isPumping = YES; 1259 | } else { 1260 | return; 1261 | } 1262 | 1263 | while ([self _innerPumpScanner]) { 1264 | 1265 | } 1266 | 1267 | _isPumping = NO; 1268 | } 1269 | 1270 | //#define NOMASK 1271 | 1272 | static const size_t SRFrameHeaderOverhead = 32; 1273 | 1274 | - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; 1275 | { 1276 | [self assertOnWorkQueue]; 1277 | 1278 | NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); 1279 | 1280 | size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; 1281 | 1282 | NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; 1283 | if (!frame) { 1284 | [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; 1285 | return; 1286 | } 1287 | uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; 1288 | 1289 | // set fin 1290 | frame_buffer[0] = SRFinMask | opcode; 1291 | 1292 | BOOL useMask = YES; 1293 | #ifdef NOMASK 1294 | useMask = NO; 1295 | #endif 1296 | 1297 | if (useMask) { 1298 | // set the mask and header 1299 | frame_buffer[1] |= SRMaskMask; 1300 | } 1301 | 1302 | size_t frame_buffer_size = 2; 1303 | 1304 | const uint8_t *unmasked_payload = NULL; 1305 | if ([data isKindOfClass:[NSData class]]) { 1306 | unmasked_payload = (uint8_t *)[data bytes]; 1307 | } else if ([data isKindOfClass:[NSString class]]) { 1308 | unmasked_payload = (const uint8_t *)[data UTF8String]; 1309 | } 1310 | 1311 | if (payloadLength < 126) { 1312 | frame_buffer[1] |= payloadLength; 1313 | } else if (payloadLength <= UINT16_MAX) { 1314 | frame_buffer[1] |= 126; 1315 | *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); 1316 | frame_buffer_size += sizeof(uint16_t); 1317 | } else { 1318 | frame_buffer[1] |= 127; 1319 | *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); 1320 | frame_buffer_size += sizeof(uint64_t); 1321 | } 1322 | 1323 | if (!useMask) { 1324 | for (size_t i = 0; i < payloadLength; i++) { 1325 | frame_buffer[frame_buffer_size] = unmasked_payload[i]; 1326 | frame_buffer_size += 1; 1327 | } 1328 | } else { 1329 | uint8_t *mask_key = frame_buffer + frame_buffer_size; 1330 | SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); 1331 | frame_buffer_size += sizeof(uint32_t); 1332 | 1333 | // TODO: could probably optimize this with SIMD 1334 | for (size_t i = 0; i < payloadLength; i++) { 1335 | frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; 1336 | frame_buffer_size += 1; 1337 | } 1338 | } 1339 | 1340 | assert(frame_buffer_size <= [frame length]); 1341 | frame.length = frame_buffer_size; 1342 | 1343 | [self _writeData:frame]; 1344 | } 1345 | 1346 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; 1347 | { 1348 | if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { 1349 | 1350 | NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; 1351 | if (sslCerts) { 1352 | SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; 1353 | if (secTrust) { 1354 | NSInteger numCerts = SecTrustGetCertificateCount(secTrust); 1355 | for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { 1356 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); 1357 | NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); 1358 | 1359 | for (id ref in sslCerts) { 1360 | SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; 1361 | NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); 1362 | 1363 | if ([trustedCertData isEqualToData:certData]) { 1364 | _pinnedCertFound = YES; 1365 | break; 1366 | } 1367 | } 1368 | } 1369 | } 1370 | 1371 | if (!_pinnedCertFound) { 1372 | dispatch_async(_workQueue, ^{ 1373 | [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]]; 1374 | }); 1375 | return; 1376 | } 1377 | } 1378 | } 1379 | 1380 | dispatch_async(_workQueue, ^{ 1381 | switch (eventCode) { 1382 | case NSStreamEventOpenCompleted: { 1383 | SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); 1384 | if (self.readyState >= SR_CLOSING) { 1385 | return; 1386 | } 1387 | assert(_readBuffer); 1388 | 1389 | if (self.readyState == SR_CONNECTING && aStream == _inputStream) { 1390 | [self didConnect]; 1391 | } 1392 | [self _pumpWriting]; 1393 | [self _pumpScanner]; 1394 | break; 1395 | } 1396 | 1397 | case NSStreamEventErrorOccurred: { 1398 | SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); 1399 | /// TODO specify error better! 1400 | [self _failWithError:aStream.streamError]; 1401 | _readBufferOffset = 0; 1402 | [_readBuffer setLength:0]; 1403 | break; 1404 | 1405 | } 1406 | 1407 | case NSStreamEventEndEncountered: { 1408 | [self _pumpScanner]; 1409 | SRFastLog(@"NSStreamEventEndEncountered %@", aStream); 1410 | if (aStream.streamError) { 1411 | [self _failWithError:aStream.streamError]; 1412 | } else { 1413 | if (self.readyState != SR_CLOSED) { 1414 | self.readyState = SR_CLOSED; 1415 | _selfRetain = nil; 1416 | } 1417 | 1418 | if (!_sentClose && !_failed) { 1419 | _sentClose = YES; 1420 | // If we get closed in this state it's probably not clean because we should be sending this when we send messages 1421 | [self _performDelegateBlock:^{ 1422 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1423 | [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; 1424 | } 1425 | }]; 1426 | } 1427 | } 1428 | 1429 | break; 1430 | } 1431 | 1432 | case NSStreamEventHasBytesAvailable: { 1433 | SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); 1434 | const int bufferSize = 2048; 1435 | uint8_t buffer[bufferSize]; 1436 | 1437 | while (_inputStream.hasBytesAvailable) { 1438 | NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize]; 1439 | 1440 | if (bytes_read > 0) { 1441 | [_readBuffer appendBytes:buffer length:bytes_read]; 1442 | } else if (bytes_read < 0) { 1443 | [self _failWithError:_inputStream.streamError]; 1444 | } 1445 | 1446 | if (bytes_read != bufferSize) { 1447 | break; 1448 | } 1449 | }; 1450 | [self _pumpScanner]; 1451 | break; 1452 | } 1453 | 1454 | case NSStreamEventHasSpaceAvailable: { 1455 | SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); 1456 | [self _pumpWriting]; 1457 | break; 1458 | } 1459 | 1460 | default: 1461 | SRFastLog(@"(default) %@", aStream); 1462 | break; 1463 | } 1464 | }); 1465 | } 1466 | 1467 | @end 1468 | 1469 | 1470 | @implementation SRIOConsumer 1471 | 1472 | @synthesize bytesNeeded = _bytesNeeded; 1473 | @synthesize consumer = _scanner; 1474 | @synthesize handler = _handler; 1475 | @synthesize readToCurrentFrame = _readToCurrentFrame; 1476 | @synthesize unmaskBytes = _unmaskBytes; 1477 | 1478 | - (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1479 | { 1480 | _scanner = [scanner copy]; 1481 | _handler = [handler copy]; 1482 | _bytesNeeded = bytesNeeded; 1483 | _readToCurrentFrame = readToCurrentFrame; 1484 | _unmaskBytes = unmaskBytes; 1485 | assert(_scanner || _bytesNeeded); 1486 | } 1487 | 1488 | 1489 | @end 1490 | 1491 | 1492 | @implementation SRIOConsumerPool { 1493 | NSUInteger _poolSize; 1494 | NSMutableArray *_bufferedConsumers; 1495 | } 1496 | 1497 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 1498 | { 1499 | self = [super init]; 1500 | if (self) { 1501 | _poolSize = poolSize; 1502 | _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; 1503 | } 1504 | return self; 1505 | } 1506 | 1507 | - (id)init 1508 | { 1509 | return [self initWithBufferCapacity:8]; 1510 | } 1511 | 1512 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1513 | { 1514 | SRIOConsumer *consumer = nil; 1515 | if (_bufferedConsumers.count) { 1516 | consumer = [_bufferedConsumers lastObject]; 1517 | [_bufferedConsumers removeLastObject]; 1518 | } else { 1519 | consumer = [[SRIOConsumer alloc] init]; 1520 | } 1521 | 1522 | [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; 1523 | 1524 | return consumer; 1525 | } 1526 | 1527 | - (void)returnConsumer:(SRIOConsumer *)consumer; 1528 | { 1529 | if (_bufferedConsumers.count < _poolSize) { 1530 | [_bufferedConsumers addObject:consumer]; 1531 | } 1532 | } 1533 | 1534 | @end 1535 | 1536 | 1537 | @implementation NSURLRequest (CertificateAdditions) 1538 | 1539 | - (NSArray *)SR_SSLPinnedCertificates; 1540 | { 1541 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1542 | } 1543 | 1544 | @end 1545 | 1546 | @implementation NSMutableURLRequest (CertificateAdditions) 1547 | 1548 | - (NSArray *)SR_SSLPinnedCertificates; 1549 | { 1550 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1551 | } 1552 | 1553 | - (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; 1554 | { 1555 | [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1556 | } 1557 | 1558 | @end 1559 | 1560 | @implementation NSURL (SRWebSocket) 1561 | 1562 | - (NSString *)SR_origin; 1563 | { 1564 | NSString *scheme = [self.scheme lowercaseString]; 1565 | 1566 | if ([scheme isEqualToString:@"wss"]) { 1567 | scheme = @"https"; 1568 | } else if ([scheme isEqualToString:@"ws"]) { 1569 | scheme = @"http"; 1570 | } 1571 | 1572 | if (self.port) { 1573 | return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.host, self.port]; 1574 | } else { 1575 | return [NSString stringWithFormat:@"%@://%@/", scheme, self.host]; 1576 | } 1577 | } 1578 | 1579 | @end 1580 | 1581 | //#define SR_ENABLE_LOG 1582 | 1583 | static inline void SRFastLog(NSString *format, ...) { 1584 | #ifdef SR_ENABLE_LOG 1585 | __block va_list arg_list; 1586 | va_start (arg_list, format); 1587 | 1588 | NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; 1589 | 1590 | va_end(arg_list); 1591 | 1592 | NSLog(@"[SR] %@", formattedString); 1593 | #endif 1594 | } 1595 | 1596 | 1597 | #ifdef HAS_ICU 1598 | 1599 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1600 | if ([data length] > INT32_MAX) { 1601 | // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. 1602 | return -1; 1603 | } 1604 | 1605 | int32_t size = (int32_t)[data length]; 1606 | 1607 | const void * contents = [data bytes]; 1608 | const uint8_t *str = (const uint8_t *)contents; 1609 | 1610 | UChar32 codepoint = 1; 1611 | int32_t offset = 0; 1612 | int32_t lastOffset = 0; 1613 | while(offset < size && codepoint > 0) { 1614 | lastOffset = offset; 1615 | U8_NEXT(str, offset, size, codepoint); 1616 | } 1617 | 1618 | if (codepoint == -1) { 1619 | // Check to see if the last byte is valid or whether it was just continuing 1620 | if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { 1621 | 1622 | size = -1; 1623 | } else { 1624 | uint8_t leadByte = str[lastOffset]; 1625 | U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); 1626 | 1627 | for (int i = lastOffset + 1; i < offset; i++) { 1628 | if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { 1629 | size = -1; 1630 | } 1631 | } 1632 | 1633 | if (size != -1) { 1634 | size = lastOffset; 1635 | } 1636 | } 1637 | } 1638 | 1639 | if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { 1640 | size = -1; 1641 | } 1642 | 1643 | return size; 1644 | } 1645 | 1646 | #else 1647 | 1648 | // This is a hack, and probably not optimal 1649 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1650 | static const int maxCodepointSize = 3; 1651 | 1652 | for (int i = 0; i < maxCodepointSize; i++) { 1653 | NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; 1654 | if (str) { 1655 | return data.length - i; 1656 | } 1657 | } 1658 | 1659 | return -1; 1660 | } 1661 | 1662 | #endif 1663 | 1664 | static _SRRunLoopThread *networkThread = nil; 1665 | static NSRunLoop *networkRunLoop = nil; 1666 | 1667 | @implementation NSRunLoop (SRWebSocket) 1668 | 1669 | + (NSRunLoop *)SR_networkRunLoop { 1670 | static dispatch_once_t onceToken; 1671 | dispatch_once(&onceToken, ^{ 1672 | networkThread = [[_SRRunLoopThread alloc] init]; 1673 | networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; 1674 | [networkThread start]; 1675 | networkRunLoop = networkThread.runLoop; 1676 | }); 1677 | 1678 | return networkRunLoop; 1679 | } 1680 | 1681 | @end 1682 | 1683 | 1684 | @implementation _SRRunLoopThread { 1685 | dispatch_group_t _waitGroup; 1686 | } 1687 | 1688 | @synthesize runLoop = _runLoop; 1689 | 1690 | - (void)dealloc 1691 | { 1692 | sr_dispatch_release(_waitGroup); 1693 | } 1694 | 1695 | - (id)init 1696 | { 1697 | self = [super init]; 1698 | if (self) { 1699 | _waitGroup = dispatch_group_create(); 1700 | dispatch_group_enter(_waitGroup); 1701 | } 1702 | return self; 1703 | } 1704 | 1705 | - (void)main; 1706 | { 1707 | @autoreleasepool { 1708 | _runLoop = [NSRunLoop currentRunLoop]; 1709 | dispatch_group_leave(_waitGroup); 1710 | 1711 | NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture] interval:0.0 target:nil selector:nil userInfo:nil repeats:NO]; 1712 | [_runLoop addTimer:timer forMode:NSDefaultRunLoopMode]; 1713 | 1714 | while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { 1715 | 1716 | } 1717 | assert(NO); 1718 | } 1719 | } 1720 | 1721 | - (NSRunLoop *)runLoop; 1722 | { 1723 | dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); 1724 | return _runLoop; 1725 | } 1726 | 1727 | @end 1728 | -------------------------------------------------------------------------------- /socketio/socketio/SocketRocket/SocketRocket-Prefix.pch: -------------------------------------------------------------------------------- 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 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #ifdef __OBJC__ 22 | #import 23 | #endif 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /socketio/socketio/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // socketio 4 | // 5 | // Created by Derek J. Kinsman on 2014-08-11. 6 | // Copyright (c) 2014 Teehan + Lax. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | 12 | class ViewController: UIViewController, SRWebSocketDelegate { 13 | 14 | var socketio:SRWebSocket? 15 | let server = "example.com" // don't include http:// 16 | let session:NSURLSession? 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | let button = UIButton.buttonWithType(UIButtonType.System) as UIButton 23 | button.frame = CGRectMake(CGRectGetWidth(view.frame)/2-100, CGRectGetHeight(view.frame)/2-40, 200, 80) 24 | button.backgroundColor = UIColor(red: 0.2421875, green: 0.64453125, blue: 0.8046875, alpha: 1) 25 | button.setTitle("Socket.IO", forState: UIControlState.Normal) 26 | button.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 1), forState: UIControlState.Normal) 27 | button.setTitleColor(UIColor(red: 0, green: 0, blue: 0, alpha: 1), forState: UIControlState.Highlighted) 28 | button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside) 29 | self.view.addSubview(button) 30 | } 31 | 32 | func buttonAction(sender:UIButton!) { 33 | var dict = [ 34 | "name": "test", 35 | "args": [["Button": "Pressed"]] 36 | ] 37 | 38 | /* 39 | * What you would see in a traditional 40 | * Socket.IO app.js file. 41 | * 42 | * socket.emit("test", { 43 | * Button: "Pressed" 44 | * }); 45 | * 46 | * 47 | */ 48 | 49 | var jsonSendError:NSError? 50 | var jsonSend = NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(0), error: &jsonSendError) 51 | var jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding) 52 | println("JSON SENT \(jsonString)") 53 | 54 | let str:NSString = "5:::\(jsonString)" 55 | socketio?.send(str) 56 | } 57 | 58 | // SWIFT REQUIREMENTS 59 | required init(coder aDecoder: NSCoder!) { 60 | super.init(coder: aDecoder) 61 | } 62 | 63 | override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) { 64 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 65 | } 66 | // SWIFT REQUIREMENTS 67 | 68 | override init() { 69 | 70 | let sessionConfig:NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration() 71 | sessionConfig.allowsCellularAccess = true 72 | sessionConfig.HTTPAdditionalHeaders = ["Content-Type": "application/json"] 73 | sessionConfig.timeoutIntervalForRequest = 30 74 | sessionConfig.timeoutIntervalForResource = 60 75 | sessionConfig.HTTPMaximumConnectionsPerHost = 1 76 | 77 | super.init() 78 | 79 | session = NSURLSession(configuration: sessionConfig) 80 | 81 | initHandshake() 82 | } 83 | 84 | func initHandshake() { 85 | let time:NSTimeInterval = NSDate().timeIntervalSince1970 * 1000 86 | 87 | var endpoint = "http://\(server)/socket.io/1?t=\(time)" 88 | 89 | var handshakeTask:NSURLSessionTask = session!.dataTaskWithURL(NSURL.URLWithString(endpoint), completionHandler: { (data:NSData!, response:NSURLResponse!, error:NSError!) in 90 | if !error { 91 | let stringData:NSString = NSString(data: data, encoding: NSUTF8StringEncoding) 92 | let handshakeToken:NSString = stringData.componentsSeparatedByString(":")[0] as NSString 93 | println("HANDSHAKE \(handshakeToken)") 94 | 95 | self.socketConnect(handshakeToken) 96 | } 97 | }) 98 | handshakeTask.resume() 99 | } 100 | 101 | func socketConnect(token:NSString) { 102 | socketio = SRWebSocket(URLRequest: NSURLRequest(URL: NSURL(string: "ws://\(server)/socket.io/1/websocket/\(token)"))) 103 | socketio!.delegate = self 104 | socketio!.open() 105 | } 106 | 107 | func webSocket(webSocket: SRWebSocket!, didReceiveMessage message: AnyObject!) { 108 | // All incoming messages ( socket.on() ) are received in this function. Parsed with JSON 109 | println("MESSAGE: \(message)") 110 | 111 | var jsonError:NSError? 112 | let messageArray = (message as NSString).componentsSeparatedByString(":::") 113 | let data:NSData = messageArray[messageArray.endIndex - 1].dataUsingEncoding(NSUTF8StringEncoding) 114 | var json:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) 115 | 116 | if json != nil { 117 | let event: NSString = json!["name"] as NSString 118 | let args: NSDictionary = (json!["args"] as NSArray)[0] as NSDictionary 119 | 120 | if (event.isEqualToString("one")) { 121 | didReceiveEventOne(args) 122 | } 123 | else if (event.isEqualToString("two")) { 124 | didReceiveEventTwo(args) 125 | } 126 | else if (event.isEqualToString("three")) { 127 | didReceiveEventThree(args) 128 | } 129 | } 130 | } 131 | 132 | func didReceiveEventOne(args: NSDictionary) { 133 | var dict = [ 134 | "name": "event", 135 | "args": [["Method": "One"]] 136 | ] 137 | var jsonSendError:NSError? 138 | var jsonSend = NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(0), error: &jsonSendError) 139 | var jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding) 140 | println("JSON SENT \(jsonString)") 141 | let str:NSString = "5:::\(jsonString)" 142 | socketio?.send(str) 143 | } 144 | 145 | func didReceiveEventTwo(args: NSDictionary) { 146 | var dict = [ 147 | "name": "event", 148 | "args": [["Method": "Two"]] 149 | ] 150 | var jsonSendError:NSError? 151 | var jsonSend = NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(0), error: &jsonSendError) 152 | var jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding) 153 | println("JSON SENT \(jsonString)") 154 | let str:NSString = "5:::\(jsonString)" 155 | socketio?.send(str) 156 | } 157 | 158 | func didReceiveEventThree(args: NSDictionary) { 159 | var dict = [ 160 | "name": "event", 161 | "args": [["Method": "Three"]] 162 | ] 163 | var jsonSendError:NSError? 164 | var jsonSend = NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(0), error: &jsonSendError) 165 | var jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding) 166 | println("JSON SENT \(jsonString)") 167 | let str:NSString = "5:::\(jsonString)" 168 | socketio?.send(str) 169 | } 170 | 171 | override func didReceiveMemoryWarning() { 172 | super.didReceiveMemoryWarning() 173 | } 174 | 175 | } 176 | 177 | -------------------------------------------------------------------------------- /socketio/socketio/socketio-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "SocketRocket/SRWebSocket.h" 6 | -------------------------------------------------------------------------------- /socketio/socketioTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.teehanlax.labs.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /socketio/socketioTests/socketioTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // socketioTests.swift 3 | // socketioTests 4 | // 5 | // Created by Derek J. Kinsman on 2014-08-11. 6 | // Copyright (c) 2014 Teehan + Lax. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class socketioTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------