From 188154076cbcf141441abdb26a16ea30faf2e859 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Wed, 27 Nov 2019 17:25:10 +0800 Subject: [PATCH 01/21] build: update cli --- main.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/main.swift b/main.swift index ae4bd9bb6..cf0b79066 100755 --- a/main.swift +++ b/main.swift @@ -72,6 +72,21 @@ class Task { } } +class OpenTask: Task { + convenience init(arguments: [String] = []) { + self.init( + launchPath: "/usr/bin/env", + arguments: ["open"] + arguments) + } + + static func url(_ urlString: String) throws { + guard let _ = URL(string: urlString), + OpenTask(arguments: [urlString]).excute() else { + throw TaskError() + } + } +} + class XcodebuildTask: Task { static let projectPath = "./AVOS/AVOS.xcodeproj" @@ -515,6 +530,7 @@ class AppledocTask: Task { try generateDocumentation(currentVersion: currentVersion) try moveGeneratedDocumentationToRepo() try commitPush() + try OpenTask.url("http://jenkins.avoscloud.com/job/cn-api-doc-prod-ucloud/build") } } From 59ec51788228b452f5031cf5f93fe3c0b6556f4d Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 9 Dec 2019 16:46:46 +0800 Subject: [PATCH 02/21] test: update case --- AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift b/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift index fe8e0ed25..8a2f304f3 100644 --- a/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift +++ b/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift @@ -837,6 +837,7 @@ class AVIMMessageTestCase: LCIMTestBase { let text: String = "test" let customMessage: AVIMCustomTypedMessage = AVIMCustomTypedMessage() customMessage.text = text + customMessage.setObject("value", forKey: "key") semaphore.increment(2) @@ -861,6 +862,7 @@ class AVIMMessageTestCase: LCIMTestBase { XCTAssertTrue(message.readTimestamp == 0) XCTAssertFalse(message.transient) XCTAssertNil(message.updatedAt) + XCTAssertEqual(message.object(forKey: "key") as? String, "value") } normalConv.send(customMessage, callback: { (succeeded: Bool, error: Error?) in From 7005ad3231d9ad355303ed49eccd7351292b1c98 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 20 Dec 2019 20:27:21 +0800 Subject: [PATCH 03/21] chore: update third party library AFNetworking close #593 --- AVOS/AVOS.xcodeproj/project.pbxproj | 270 +++++----- AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m | 16 +- .../LCNetworking/LCCompatibilityMacros.h | 37 ++ .../LCNetworking/LCHTTPSessionManager.h | 37 +- .../LCNetworking/LCHTTPSessionManager.m | 25 +- .../LCNetworkReachabilityManager.h | 62 ++- .../LCNetworkReachabilityManager.m | 94 ++-- .../ThirdParty/LCNetworking/LCNetworking.h | 8 +- .../LCNetworking/LCSecurityPolicy.h | 34 +- .../LCNetworking/LCSecurityPolicy.m | 59 +- .../LCNetworking/LCURLRequestSerialization.h | 44 +- .../LCNetworking/LCURLRequestSerialization.m | 212 ++++---- .../LCNetworking/LCURLResponseSerialization.h | 59 +- .../LCNetworking/LCURLResponseSerialization.m | 144 ++--- .../LCNetworking/LCURLSessionManager.h | 35 +- .../LCNetworking/LCURLSessionManager.m | 507 +++++++----------- .../WebSocket/AVIMWebSocketWrapper.m | 14 +- 17 files changed, 814 insertions(+), 843 deletions(-) create mode 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h mode change 100755 => 100644 AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m diff --git a/AVOS/AVOS.xcodeproj/project.pbxproj b/AVOS/AVOS.xcodeproj/project.pbxproj index 974b07b3c..5a8f56de6 100644 --- a/AVOS/AVOS.xcodeproj/project.pbxproj +++ b/AVOS/AVOS.xcodeproj/project.pbxproj @@ -568,58 +568,6 @@ 83F745FD1B9177F100437259 /* LCIMConversationCacheStoreSQL.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F745FC1B9177F100437259 /* LCIMConversationCacheStoreSQL.h */; }; 83F9A2C71CE014430002E21B /* LCRouter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F9A2C51CE014430002E21B /* LCRouter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83F9A2C81CE014430002E21B /* LCRouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F9A2C61CE014430002E21B /* LCRouter.m */; }; - 83FCB99F1CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */; }; - 83FCB9A11CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */; }; - 83FCB9A21CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */; }; - 83FCB9A31CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */; }; - 83FCB9A41CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */; }; - 83FCB9A61CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */; }; - 83FCB9A71CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */; }; - 83FCB9A81CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */; }; - 83FCB9A91CEDC57C007D8712 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9941CEDC57C007D8712 /* LCNetworking.h */; }; - 83FCB9AB1CEDC57C007D8712 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9941CEDC57C007D8712 /* LCNetworking.h */; }; - 83FCB9AC1CEDC57C007D8712 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9941CEDC57C007D8712 /* LCNetworking.h */; }; - 83FCB9AD1CEDC57C007D8712 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9941CEDC57C007D8712 /* LCNetworking.h */; }; - 83FCB9AE1CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */; }; - 83FCB9B01CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */; }; - 83FCB9B11CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */; }; - 83FCB9B21CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */; }; - 83FCB9B31CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */; }; - 83FCB9B51CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */; }; - 83FCB9B61CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */; }; - 83FCB9B71CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */; }; - 83FCB9B81CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */; }; - 83FCB9BA1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */; }; - 83FCB9BB1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */; }; - 83FCB9BC1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */; }; - 83FCB9BD1CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */; }; - 83FCB9BF1CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */; }; - 83FCB9C01CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */; }; - 83FCB9C11CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */; }; - 83FCB9C21CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */; }; - 83FCB9C41CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */; }; - 83FCB9C51CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */; }; - 83FCB9C61CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */; }; - 83FCB9C71CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */; }; - 83FCB9C91CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */; }; - 83FCB9CA1CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */; }; - 83FCB9CB1CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */; }; - 83FCB9CC1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */; }; - 83FCB9CE1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */; }; - 83FCB9CF1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */; }; - 83FCB9D01CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */; }; - 83FCB9D11CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */; }; - 83FCB9D31CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */; }; - 83FCB9D41CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */; }; - 83FCB9D51CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */; }; - 83FCB9D61CEDC57C007D8712 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */; }; - 83FCB9D81CEDC57C007D8712 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */; }; - 83FCB9D91CEDC57C007D8712 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */; }; - 83FCB9DA1CEDC57C007D8712 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */; }; - 83FCB9DB1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */; }; - 83FCB9DD1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */; }; - 83FCB9DE1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */; }; - 83FCB9DF1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */; }; 8C2FED541A67C2DA0056F945 /* AVIMGeneralObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C2FED521A67C2DA0056F945 /* AVIMGeneralObject.h */; }; 8C2FED551A67C2DA0056F945 /* AVIMGeneralObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FED531A67C2DA0056F945 /* AVIMGeneralObject.m */; }; 8C630C811A6EB259008F1B00 /* AVIMErrorUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C630C7F1A6EB259008F1B00 /* AVIMErrorUtil.h */; }; @@ -850,6 +798,62 @@ D34FD72C2068CFE900B7C11B /* AVLiveQuery_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D34FD72B2068CFE900B7C11B /* AVLiveQuery_Internal.h */; }; D37A04671FF36E6200AD44A9 /* testVideo.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = D37A04661FF36E6200AD44A9 /* testVideo.mp4 */; }; D37A046A1FF3705500AD44A9 /* testFile.md in Resources */ = {isa = PBXBuildFile; fileRef = D37A04691FF3705500AD44A9 /* testFile.md */; }; + D37EE4B123ACF39700AACE99 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */; }; + D37EE4B223ACF39700AACE99 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */; }; + D37EE4B323ACF39700AACE99 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */; }; + D37EE4B423ACF39700AACE99 /* LCSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */; }; + D37EE4B523ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */; }; + D37EE4B623ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */; }; + D37EE4B723ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */; }; + D37EE4B823ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */; }; + D37EE4B923ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */; }; + D37EE4BA23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */; }; + D37EE4BB23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */; }; + D37EE4BC23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */; }; + D37EE4BD23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */; }; + D37EE4BE23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */; }; + D37EE4BF23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */; }; + D37EE4C023ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */; }; + D37EE4C123ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */; }; + D37EE4C223ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */; }; + D37EE4C323ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */; }; + D37EE4C423ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */; }; + D37EE4C523ACF39700AACE99 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */; }; + D37EE4C623ACF39700AACE99 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */; }; + D37EE4C723ACF39700AACE99 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */; }; + D37EE4C823ACF39700AACE99 /* LCURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */; }; + D37EE4C923ACF39700AACE99 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A923ACF39700AACE99 /* LCNetworking.h */; }; + D37EE4CA23ACF39700AACE99 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A923ACF39700AACE99 /* LCNetworking.h */; }; + D37EE4CB23ACF39700AACE99 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A923ACF39700AACE99 /* LCNetworking.h */; }; + D37EE4CC23ACF39700AACE99 /* LCNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4A923ACF39700AACE99 /* LCNetworking.h */; }; + D37EE4CD23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */; }; + D37EE4CE23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */; }; + D37EE4CF23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */; }; + D37EE4D023ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */; }; + D37EE4D123ACF39700AACE99 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */; }; + D37EE4D223ACF39700AACE99 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */; }; + D37EE4D323ACF39700AACE99 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */; }; + D37EE4D423ACF39700AACE99 /* LCURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */; }; + D37EE4D523ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */; }; + D37EE4D623ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */; }; + D37EE4D723ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */; }; + D37EE4D823ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */; }; + D37EE4D923ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */; }; + D37EE4DA23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */; }; + D37EE4DB23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */; }; + D37EE4DC23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */; }; + D37EE4DD23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */; }; + D37EE4DE23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */; }; + D37EE4DF23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */; }; + D37EE4E023ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */; }; + D37EE4E123ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */; }; + D37EE4E223ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */; }; + D37EE4E323ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */; }; + D37EE4E423ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */; }; + D37EE4E523ACF39700AACE99 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */; }; + D37EE4E623ACF39700AACE99 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */; }; + D37EE4E723ACF39700AACE99 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */; }; + D37EE4E823ACF39700AACE99 /* LCSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */; }; D3939CC720FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D3939CC620FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h */; }; D3939CC820FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D3939CC620FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h */; }; D3C53FCC2106D84A00D48686 /* AVIMClientProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D3C53FCB2106D84A00D48686 /* AVIMClientProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1189,19 +1193,6 @@ 83F745FC1B9177F100437259 /* LCIMConversationCacheStoreSQL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCIMConversationCacheStoreSQL.h; sourceTree = ""; }; 83F9A2C51CE014430002E21B /* LCRouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCRouter.h; sourceTree = ""; }; 83F9A2C61CE014430002E21B /* LCRouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCRouter.m; sourceTree = ""; }; - 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCHTTPSessionManager.h; sourceTree = ""; }; - 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCHTTPSessionManager.m; sourceTree = ""; }; - 83FCB9941CEDC57C007D8712 /* LCNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCNetworking.h; sourceTree = ""; }; - 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCNetworkReachabilityManager.h; sourceTree = ""; }; - 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCNetworkReachabilityManager.m; sourceTree = ""; }; - 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCSecurityPolicy.h; sourceTree = ""; }; - 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCSecurityPolicy.m; sourceTree = ""; }; - 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLRequestSerialization.h; sourceTree = ""; }; - 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLRequestSerialization.m; sourceTree = ""; }; - 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLResponseSerialization.h; sourceTree = ""; }; - 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLResponseSerialization.m; sourceTree = ""; }; - 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLSessionManager.h; sourceTree = ""; }; - 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLSessionManager.m; sourceTree = ""; }; 8C1F675E1A8B379F00310E65 /* AVIMConversationQuery_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVIMConversationQuery_Internal.h; sourceTree = ""; }; 8C2FED521A67C2DA0056F945 /* AVIMGeneralObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMGeneralObject.h; sourceTree = ""; }; 8C2FED531A67C2DA0056F945 /* AVIMGeneralObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVIMGeneralObject.m; sourceTree = ""; }; @@ -1397,6 +1388,20 @@ D34FD72B2068CFE900B7C11B /* AVLiveQuery_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVLiveQuery_Internal.h; sourceTree = ""; }; D37A04661FF36E6200AD44A9 /* testVideo.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = testVideo.mp4; sourceTree = ""; }; D37A04691FF3705500AD44A9 /* testFile.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = testFile.md; sourceTree = ""; }; + D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCSecurityPolicy.m; sourceTree = ""; }; + D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLRequestSerialization.h; sourceTree = ""; }; + D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLResponseSerialization.h; sourceTree = ""; }; + D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLRequestSerialization.m; sourceTree = ""; }; + D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLResponseSerialization.m; sourceTree = ""; }; + D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCURLSessionManager.h; sourceTree = ""; }; + D37EE4A923ACF39700AACE99 /* LCNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCNetworking.h; sourceTree = ""; }; + D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCHTTPSessionManager.m; sourceTree = ""; }; + D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCURLSessionManager.m; sourceTree = ""; }; + D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCCompatibilityMacros.h; sourceTree = ""; }; + D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCHTTPSessionManager.h; sourceTree = ""; }; + D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCNetworkReachabilityManager.h; sourceTree = ""; }; + D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCNetworkReachabilityManager.m; sourceTree = ""; }; + D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCSecurityPolicy.h; sourceTree = ""; }; D3939CC620FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVIMClientInternalConversationManager_Internal.h; sourceTree = ""; }; D3C53FCB2106D84A00D48686 /* AVIMClientProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVIMClientProtocol.h; sourceTree = ""; }; D3CC5D272252242A00B3C778 /* AVQueryTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVQueryTestCase.swift; sourceTree = ""; }; @@ -1735,19 +1740,20 @@ 83FCB9911CEDC57C007D8712 /* LCNetworking */ = { isa = PBXGroup; children = ( - 83FCB9921CEDC57C007D8712 /* LCHTTPSessionManager.h */, - 83FCB9931CEDC57C007D8712 /* LCHTTPSessionManager.m */, - 83FCB9941CEDC57C007D8712 /* LCNetworking.h */, - 83FCB9951CEDC57C007D8712 /* LCNetworkReachabilityManager.h */, - 83FCB9961CEDC57C007D8712 /* LCNetworkReachabilityManager.m */, - 83FCB9971CEDC57C007D8712 /* LCSecurityPolicy.h */, - 83FCB9981CEDC57C007D8712 /* LCSecurityPolicy.m */, - 83FCB9991CEDC57C007D8712 /* LCURLRequestSerialization.h */, - 83FCB99A1CEDC57C007D8712 /* LCURLRequestSerialization.m */, - 83FCB99B1CEDC57C007D8712 /* LCURLResponseSerialization.h */, - 83FCB99C1CEDC57C007D8712 /* LCURLResponseSerialization.m */, - 83FCB99D1CEDC57C007D8712 /* LCURLSessionManager.h */, - 83FCB99E1CEDC57C007D8712 /* LCURLSessionManager.m */, + D37EE4AC23ACF39700AACE99 /* LCCompatibilityMacros.h */, + D37EE4AD23ACF39700AACE99 /* LCHTTPSessionManager.h */, + D37EE4AA23ACF39700AACE99 /* LCHTTPSessionManager.m */, + D37EE4A923ACF39700AACE99 /* LCNetworking.h */, + D37EE4AE23ACF39700AACE99 /* LCNetworkReachabilityManager.h */, + D37EE4AF23ACF39700AACE99 /* LCNetworkReachabilityManager.m */, + D37EE4B023ACF39700AACE99 /* LCSecurityPolicy.h */, + D37EE4A323ACF39600AACE99 /* LCSecurityPolicy.m */, + D37EE4A423ACF39600AACE99 /* LCURLRequestSerialization.h */, + D37EE4A623ACF39700AACE99 /* LCURLRequestSerialization.m */, + D37EE4A523ACF39600AACE99 /* LCURLResponseSerialization.h */, + D37EE4A723ACF39700AACE99 /* LCURLResponseSerialization.m */, + D37EE4A823ACF39700AACE99 /* LCURLSessionManager.h */, + D37EE4AB23ACF39700AACE99 /* LCURLSessionManager.m */, ); path = LCNetworking; sourceTree = ""; @@ -2523,6 +2529,7 @@ buildActionMask = 2147483647; files = ( 83331D461EB9A25F00CADC9C /* AVCaptcha.h in Headers */, + D37EE4BA23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */, 835092131EB1ECB7000DA884 /* AVDynamicObject.h in Headers */, 835091FE1EB1BDD0000DA884 /* AVSMS.h in Headers */, 70CA800D1BDE4314000A3B21 /* AVSearchQuery.h in Headers */, @@ -2534,7 +2541,6 @@ D34EBDCE211C20FF0092A538 /* LCRouter_Internal.h in Headers */, 70CA80031BDE4313000A3B21 /* AVPush.h in Headers */, 70CA80051BDE4313000A3B21 /* AVCloudQueryResult.h in Headers */, - 83FCB9AB1CEDC57C007D8712 /* LCNetworking.h in Headers */, 70CA80081BDE4313000A3B21 /* AVQuery.h in Headers */, 70CA80101BDE4314000A3B21 /* AVStatus.h in Headers */, 70CA80321BDE4318000A3B21 /* AVAnonymousUtils.h in Headers */, @@ -2543,11 +2549,13 @@ 83A2BD311CF33CDA0047B230 /* AVPaasClient_internal.h in Headers */, 70CA7FDB1BDE42F5000A3B21 /* AVACL.h in Headers */, 70CA7FDD1BDE42F5000A3B21 /* AVRole.h in Headers */, + D37EE4DE23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */, 70CA7FDF1BDE42F5000A3B21 /* AVAnalytics.h in Headers */, 70CA7FEA1BDE42F6000A3B21 /* AVOSCloud.h in Headers */, 70CA7FEE1BDE42F6000A3B21 /* AVCloud.h in Headers */, 70CA7FF01BDE42F6000A3B21 /* AVFile.h in Headers */, 70CA7FF11BDE42F6000A3B21 /* AVFileQuery.h in Headers */, + D37EE4DA23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */, 70CA7FF61BDE42F6000A3B21 /* AVGeoPoint.h in Headers */, 70CA7FFA1BDE4313000A3B21 /* AVObject.h in Headers */, 835C5F721D2594EA00EF0EBB /* AVAnalyticsActivity.h in Headers */, @@ -2560,23 +2568,19 @@ 70CA80021BDE4313000A3B21 /* AVInstallation_Internal.h in Headers */, 70CA80041BDE4313000A3B21 /* AVPush_Internal.h in Headers */, 70CA80061BDE4313000A3B21 /* AVCloudQueryResult_Internal.h in Headers */, - 83FCB9BA1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */, 70CA80091BDE4314000A3B21 /* AVQuery_Internal.h in Headers */, 70CA800B1BDE4314000A3B21 /* AVRequestOperation.h in Headers */, 70CA800C1BDE4314000A3B21 /* AVPaasClient.h in Headers */, 70CA800F1BDE4314000A3B21 /* LCNetworkStatistics.h in Headers */, - 83FCB9B01CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */, 8311CD871C3D25B9007DEFF8 /* AVAvailability.h in Headers */, 83331D4F1EB9A34000CADC9C /* AVDynamicObject_Internal.h in Headers */, - 83FCB9A11CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */, - 83FCB9CE1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */, 70CA801A1BDE4315000A3B21 /* LCDatabase.h in Headers */, 70CA801B1BDE4315000A3B21 /* LCDatabaseAdditions.h in Headers */, 70CA801C1BDE4315000A3B21 /* LCDatabasePool.h in Headers */, + D37EE4D623ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */, 70CA801D1BDE4315000A3B21 /* LCDatabaseQueue.h in Headers */, 70CA801E1BDE4315000A3B21 /* LCDB.h in Headers */, 70CA801F1BDE4315000A3B21 /* LCResultSet.h in Headers */, - 83FCB9D81CEDC57C007D8712 /* LCURLSessionManager.h in Headers */, 70CA80341BDE4318000A3B21 /* AVUser_Internal.h in Headers */, 70CA80351BDE4318000A3B21 /* AVErrorUtils.h in Headers */, 70CA80361BDE4318000A3B21 /* AVHelpers.h in Headers */, @@ -2603,13 +2607,16 @@ 9A404DD41CE5A9B700DEB3DC /* LCRouter.h in Headers */, 70CA7FEB1BDE42F6000A3B21 /* AVCacheManager.h in Headers */, 70CA7FEC1BDE42F6000A3B21 /* AVPersistenceUtils.h in Headers */, + D37EE4E623ACF39700AACE99 /* LCSecurityPolicy.h in Headers */, 70CA7FED1BDE42F6000A3B21 /* AVScheduler.h in Headers */, 70CA7FEF1BDE42F6000A3B21 /* AVCloud_Internal.h in Headers */, 70CA7FF21BDE42F6000A3B21 /* AVFile_Internal.h in Headers */, + D37EE4B623ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */, 830EB9F61C44D68400BA917F /* AVSaveOption.h in Headers */, + D37EE4CA23ACF39700AACE99 /* LCNetworking.h in Headers */, 70CA7FF51BDE42F6000A3B21 /* LCFileTaskManager.h in Headers */, - 83FCB9C41CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */, 8302AED71C192A0700E13F8A /* LCURLConnection.h in Headers */, + D37EE4C623ACF39700AACE99 /* LCURLSessionManager.h in Headers */, 70CA7FF71BDE42F6000A3B21 /* AVGeoPoint_Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2622,9 +2629,12 @@ 835092151EB1ECB7000DA884 /* AVDynamicObject.h in Headers */, 835092001EB1BDD0000DA884 /* AVSMS.h in Headers */, 8320F8D91C1918C60024B45E /* AVACL.h in Headers */, + D37EE4DC23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */, 8320F8DA1C1918C60024B45E /* LCDB.h in Headers */, 8320F8DB1C1918C60024B45E /* AVRole.h in Headers */, 8320F8DD1C1918C60024B45E /* AVConstants.h in Headers */, + D37EE4BC23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */, + D37EE4D823ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */, 8320F8DE1C1918C60024B45E /* AVOSCloud.h in Headers */, 8320F8DF1C1918C60024B45E /* AVCloud.h in Headers */, 8320F8E01C1918C60024B45E /* LCDatabase.h in Headers */, @@ -2638,31 +2648,28 @@ 8320F8F01C1918C60024B45E /* AVObject.h in Headers */, 8320F8F11C1918C60024B45E /* AVRelation.h in Headers */, 83331D571EB9D55100CADC9C /* NSDictionary+LeanCloud.h in Headers */, - 83FCB9C61CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */, 8320F8F21C1918C60024B45E /* AVSubclassing.h in Headers */, 8320F8F31C1918C60024B45E /* AVInstallation.h in Headers */, 8320F8F41C1918C60024B45E /* AVFileQuery.h in Headers */, + D37EE4E023ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */, 8302AED91C192A0700E13F8A /* LCURLConnection.h in Headers */, 83E5D9531C1AA7D200E61E37 /* AVAnalyticsUtils.h in Headers */, 8320F8F51C1918C60024B45E /* AVPush.h in Headers */, + D37EE4CC23ACF39700AACE99 /* LCNetworking.h in Headers */, 8320F8F61C1918C60024B45E /* AVCloudQueryResult.h in Headers */, 8320F8F71C1918C60024B45E /* AVQuery.h in Headers */, 9A404DD61CE5A9BB00DEB3DC /* LCRouter.h in Headers */, - 83FCB9DA1CEDC57C007D8712 /* LCURLSessionManager.h in Headers */, + D37EE4C823ACF39700AACE99 /* LCURLSessionManager.h in Headers */, 8320F8F81C1918C60024B45E /* AVSearchQuery.h in Headers */, - 83FCB9D01CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */, 8320F8FB1C1918C60024B45E /* LCDatabaseCoordinator.h in Headers */, 8320F8FC1C1918C60024B45E /* AVSearchSortBuilder.h in Headers */, 8320F8FD1C1918C60024B45E /* AVStatus.h in Headers */, - 83FCB9A31CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */, - 83FCB9B21CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */, 8320F8FF1C1918C60024B45E /* AVAnonymousUtils.h in Headers */, 8320F9001C1918C60024B45E /* AVUser.h in Headers */, 8320F9011C1918C60024B45E /* AVLogger.h in Headers */, 830EBA031C44F3D600BA917F /* AVSaveOption_internal.h in Headers */, 8320F9021C1918C60024B45E /* AVGlobal.h in Headers */, 83331D511EB9A34000CADC9C /* AVDynamicObject_Internal.h in Headers */, - 83FCB9BC1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */, 8320F90F1C1918C60024B45E /* AVHelpers.h in Headers */, 8320F9111C1918C60024B45E /* LCDatabaseCommon.h in Headers */, 8320F9121C1918C60024B45E /* LCResultSet.h in Headers */, @@ -2673,6 +2680,7 @@ 8320F91A1C1918C60024B45E /* AVObjectUtils.h in Headers */, 8320F91C1C1918C60024B45E /* AVRequestOperation.h in Headers */, 8311CD891C3D25B9007DEFF8 /* AVAvailability.h in Headers */, + D37EE4E823ACF39700AACE99 /* LCSecurityPolicy.h in Headers */, 8320F9211C1918C60024B45E /* LCDatabasePool.h in Headers */, 830EB9F81C44D68400BA917F /* AVSaveOption.h in Headers */, 8320F9281C1918C60024B45E /* AVFriendQuery.h in Headers */, @@ -2693,13 +2701,13 @@ 8320F9401C1918C60024B45E /* AVGeoPoint_Internal.h in Headers */, 8320F9471C1918C60024B45E /* AVDuration.h in Headers */, 8320F9481C1918C60024B45E /* UserAgent.h in Headers */, + D37EE4B823ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */, 8320F94B1C1918C60024B45E /* LCKeyValueSQL.h in Headers */, 8320F94C1C1918C60024B45E /* AVRole_Internal.h in Headers */, 8320F94D1C1918C60024B45E /* LCDatabaseMigrator.h in Headers */, 8320F94F1C1918C60024B45E /* AVCloud_Internal.h in Headers */, 8320F9511C1918C60024B45E /* AVCloudQueryResult_Internal.h in Headers */, 8320F9541C1918C60024B45E /* AVPush_Internal.h in Headers */, - 83FCB9AD1CEDC57C007D8712 /* LCNetworking.h in Headers */, D34EBDD0211C20FF0092A538 /* LCRouter_Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2721,10 +2729,10 @@ buildActionMask = 2147483647; files = ( 83331D471EB9A25F00CADC9C /* AVCaptcha.h in Headers */, + D37EE4BB23ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */, 835092141EB1ECB7000DA884 /* AVDynamicObject.h in Headers */, 835091FF1EB1BDD0000DA884 /* AVSMS.h in Headers */, 83D8CED21C17DAF50094279D /* AVACL.h in Headers */, - 83FCB9CF1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */, 83D8CED31C17DAF50094279D /* LCDB.h in Headers */, 83D8CED41C17DAF50094279D /* AVRole.h in Headers */, 830EB9F71C44D68400BA917F /* AVSaveOption.h in Headers */, @@ -2741,13 +2749,13 @@ 83D8CEDF1C17DAF50094279D /* AVGeoPoint.h in Headers */, 83D8CEE21C17DAF50094279D /* LCDatabaseAdditions.h in Headers */, 83D8CEE81C17DAF50094279D /* AVObject+Subclass.h in Headers */, + D37EE4DF23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */, 83D8CEE91C17DAF50094279D /* AVObject.h in Headers */, 83D8CEEA1C17DAF50094279D /* AVRelation.h in Headers */, 83D8CEEB1C17DAF50094279D /* AVSubclassing.h in Headers */, - 83FCB9AC1CEDC57C007D8712 /* LCNetworking.h in Headers */, 83D8CEEC1C17DAF50094279D /* AVInstallation.h in Headers */, - 83FCB9D91CEDC57C007D8712 /* LCURLSessionManager.h in Headers */, 83D8CEED1C17DAF50094279D /* AVFileQuery.h in Headers */, + D37EE4DB23ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */, 8302AED81C192A0700E13F8A /* LCURLConnection.h in Headers */, 83D8CEEE1C17DAF50094279D /* AVPush.h in Headers */, 83D8CEEF1C17DAF50094279D /* AVCloudQueryResult.h in Headers */, @@ -2769,11 +2777,10 @@ 83D8CF101C17DAF50094279D /* AVPaasClient.h in Headers */, 83D8CF111C17DAF50094279D /* AVQuery_Internal.h in Headers */, 83D8CF131C17DAF50094279D /* AVObjectUtils.h in Headers */, + D37EE4D723ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */, 83D8CF141C17DAF50094279D /* AVAnalyticsUtils.h in Headers */, 83D8CF151C17DAF50094279D /* AVRequestOperation.h in Headers */, 83D8CF171C17DAF50094279D /* AVAnalyticsImpl.h in Headers */, - 83FCB9A21CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */, - 83FCB9C51CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */, 83D8CF1A1C17DAF50094279D /* LCDatabasePool.h in Headers */, 83D8CF211C17DAF50094279D /* AVFriendQuery.h in Headers */, 83D8CF221C17DAF50094279D /* AVObject_Internal.h in Headers */, @@ -2795,19 +2802,21 @@ 83D8CF371C17DAF50094279D /* AVScheduler.h in Headers */, 8311CD881C3D25B9007DEFF8 /* AVAvailability.h in Headers */, 83D8CF391C17DAF50094279D /* AVGeoPoint_Internal.h in Headers */, - 83FCB9B11CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */, 83D8CF3F1C17DAF50094279D /* AVReachability.h in Headers */, 83D8CF401C17DAF50094279D /* AVDuration.h in Headers */, - 83FCB9BB1CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */, 83D8CF411C17DAF50094279D /* UserAgent.h in Headers */, 83D8CF441C17DAF50094279D /* LCKeyValueSQL.h in Headers */, 83D8CF451C17DAF50094279D /* AVRole_Internal.h in Headers */, + D37EE4E723ACF39700AACE99 /* LCSecurityPolicy.h in Headers */, 830EBA021C44F3D600BA917F /* AVSaveOption_internal.h in Headers */, 83D8CF461C17DAF50094279D /* LCDatabaseMigrator.h in Headers */, 83D8CF481C17DAF50094279D /* AVCloud_Internal.h in Headers */, + D37EE4B723ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */, 83D8CF491C17DAF50094279D /* AVAnalytics_Internal.h in Headers */, + D37EE4CB23ACF39700AACE99 /* LCNetworking.h in Headers */, 83D8CF4A1C17DAF50094279D /* AVCloudQueryResult_Internal.h in Headers */, 83D8CF4B1C17DAF50094279D /* AVAnalyticsActivity.h in Headers */, + D37EE4C723ACF39700AACE99 /* LCURLSessionManager.h in Headers */, 83D8CF4D1C17DAF50094279D /* AVPush_Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2817,6 +2826,7 @@ buildActionMask = 2147483647; files = ( 83331D451EB9A25F00CADC9C /* AVCaptcha.h in Headers */, + D37EE4B923ACF39700AACE99 /* LCURLResponseSerialization.h in Headers */, 835092121EB1ECB7000DA884 /* AVDynamicObject.h in Headers */, 835091FD1EB1BDD0000DA884 /* AVSMS.h in Headers */, 8C841AAD1A5A7A0000C5C6C4 /* AVACL.h in Headers */, @@ -2836,13 +2846,14 @@ 8C841AE71A5A7A0000C5C6C4 /* AVGeoPoint.h in Headers */, 83DF02B21AF86032000E289C /* LCDatabaseAdditions.h in Headers */, 8C841B171A5A7A0000C5C6C4 /* AVObject+Subclass.h in Headers */, + D37EE4DD23ACF39700AACE99 /* LCNetworkReachabilityManager.h in Headers */, 8C841B181A5A7A0000C5C6C4 /* AVObject.h in Headers */, 8C841B1D1A5A7A0000C5C6C4 /* AVRelation.h in Headers */, 8C841B201A5A7A0000C5C6C4 /* AVSubclassing.h in Headers */, 8C841B211A5A7A0000C5C6C4 /* AVInstallation.h in Headers */, 701881F61BC6777200432D43 /* AVFileQuery.h in Headers */, + D37EE4D923ACF39700AACE99 /* LCHTTPSessionManager.h in Headers */, 8302AED51C192A0700E13F8A /* LCURLConnection.h in Headers */, - 83FCB99F1CEDC57C007D8712 /* LCHTTPSessionManager.h in Headers */, 8C841B241A5A7A0000C5C6C4 /* AVPush.h in Headers */, 8C841B271A5A7A0000C5C6C4 /* AVCloudQueryResult.h in Headers */, 8C841B2C1A5A7A0000C5C6C4 /* AVQuery.h in Headers */, @@ -2851,7 +2862,6 @@ 8C841B381A5A7A0000C5C6C4 /* AVSearchSortBuilder.h in Headers */, 8C841B3A1A5A7A0000C5C6C4 /* AVStatus.h in Headers */, 8C841B571A5A7A0000C5C6C4 /* AVAnonymousUtils.h in Headers */, - 83FCB9A91CEDC57C007D8712 /* LCNetworking.h in Headers */, 8C841B591A5A7A0000C5C6C4 /* AVUser.h in Headers */, 8C841B601A5A7A0000C5C6C4 /* AVLogger.h in Headers */, 8C841AC31A5A7A0000C5C6C4 /* AVGlobal.h in Headers */, @@ -2862,13 +2872,12 @@ 8C841AE01A5A7A0000C5C6C4 /* AVFile_Internal.h in Headers */, 8C841B321A5A7A0000C5C6C4 /* AVPaasClient.h in Headers */, 8C841B2E1A5A7A0000C5C6C4 /* AVQuery_Internal.h in Headers */, - 83FCB9D61CEDC57C007D8712 /* LCURLSessionManager.h in Headers */, 8C841B1B1A5A7A0000C5C6C4 /* AVObjectUtils.h in Headers */, 8C841ABC1A5A7A0000C5C6C4 /* AVAnalyticsUtils.h in Headers */, + D37EE4D523ACF39700AACE99 /* LCCompatibilityMacros.h in Headers */, 8C841B301A5A7A0000C5C6C4 /* AVRequestOperation.h in Headers */, 8C841AB81A5A7A0000C5C6C4 /* AVAnalyticsImpl.h in Headers */, 83DF02B41AF86032000E289C /* LCDatabasePool.h in Headers */, - 83FCB9C21CEDC57C007D8712 /* LCURLRequestSerialization.h in Headers */, 8C841B2A1A5A7A0000C5C6C4 /* AVFriendQuery.h in Headers */, 8C841B1A1A5A7A0000C5C6C4 /* AVObject_Internal.h in Headers */, 8C841B1F1A5A7A0000C5C6C4 /* AVRelation_Internal.h in Headers */, @@ -2887,23 +2896,24 @@ 83F9A2C71CE014430002E21B /* LCRouter.h in Headers */, 8C841ABA1A5A7A0000C5C6C4 /* AVAnalyticsSession.h in Headers */, 83331D4E1EB9A34000CADC9C /* AVDynamicObject_Internal.h in Headers */, - 83FCB9B81CEDC57C007D8712 /* LCSecurityPolicy.h in Headers */, 8C841ACC1A5A7A0000C5C6C4 /* AVScheduler.h in Headers */, 8311CD851C3D25B9007DEFF8 /* AVAvailability.h in Headers */, 8C841AE91A5A7A0000C5C6C4 /* AVGeoPoint_Internal.h in Headers */, 8C841B621A5A7A0000C5C6C4 /* AVReachability.h in Headers */, - 83FCB9CC1CEDC57C007D8712 /* LCURLResponseSerialization.h in Headers */, 8C841ABE1A5A7A0000C5C6C4 /* AVDuration.h in Headers */, 8C841B661A5A7A0000C5C6C4 /* UserAgent.h in Headers */, 838DD7A31B3D3FD100C95897 /* LCKeyValueSQL.h in Headers */, 8C841AB21A5A7A0000C5C6C4 /* AVRole_Internal.h in Headers */, + D37EE4E523ACF39700AACE99 /* LCSecurityPolicy.h in Headers */, 830EB9FF1C44F3D600BA917F /* AVSaveOption_internal.h in Headers */, 830E7CFC1B1CAD3B005F4B22 /* LCDatabaseMigrator.h in Headers */, 8C841AD01A5A7A0000C5C6C4 /* AVCloud_Internal.h in Headers */, + D37EE4B523ACF39700AACE99 /* LCURLRequestSerialization.h in Headers */, 8C841AB51A5A7A0000C5C6C4 /* AVAnalytics_Internal.h in Headers */, - 83FCB9AE1CEDC57C007D8712 /* LCNetworkReachabilityManager.h in Headers */, + D37EE4C923ACF39700AACE99 /* LCNetworking.h in Headers */, 8C841B291A5A7A0000C5C6C4 /* AVCloudQueryResult_Internal.h in Headers */, 8C841AB61A5A7A0000C5C6C4 /* AVAnalyticsActivity.h in Headers */, + D37EE4C523ACF39700AACE99 /* LCURLSessionManager.h in Headers */, 8C841B261A5A7A0000C5C6C4 /* AVPush_Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3539,10 +3549,10 @@ 70CA7FA01BDE374C000A3B21 /* LCDatabasePool.m in Sources */, 70CA7FA11BDE374C000A3B21 /* LCDatabaseQueue.m in Sources */, 70CA7FA21BDE374C000A3B21 /* LCResultSet.m in Sources */, - 83FCB9A61CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */, 830EB9FB1C44D68400BA917F /* AVSaveOption.m in Sources */, 70CA7FA91BDE374C000A3B21 /* AVAnonymousUtils.m in Sources */, 70CA7FAA1BDE374C000A3B21 /* AVUser.m in Sources */, + D37EE4CE23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */, 70CA7FAB1BDE374C000A3B21 /* AVErrorUtils.m in Sources */, 70CA7FAC1BDE374C000A3B21 /* AVLogger.m in Sources */, 70CA7FAD1BDE374C000A3B21 /* AVReachability.m in Sources */, @@ -3556,6 +3566,7 @@ 70CA7F741BDE3721000A3B21 /* AVAnalytics.m in Sources */, 70CA7F751BDE3721000A3B21 /* AVAnalyticsActivity.m in Sources */, 70CA7F761BDE3721000A3B21 /* AVAnalyticsImpl.m in Sources */, + D37EE4BE23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */, 70CA7F771BDE3721000A3B21 /* AVAnalyticsSession.m in Sources */, 70CA7F781BDE3721000A3B21 /* AVAnalyticsUtils.m in Sources */, 70CA7F791BDE3721000A3B21 /* AVDuration.m in Sources */, @@ -3566,6 +3577,8 @@ 70CA7F7E1BDE3721000A3B21 /* AVScheduler.m in Sources */, 70CA7F7F1BDE3721000A3B21 /* AVCloud.m in Sources */, 70CA7F801BDE3721000A3B21 /* AVFile.m in Sources */, + D37EE4D223ACF39700AACE99 /* LCURLSessionManager.m in Sources */, + D37EE4E223ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */, 70CA7F811BDE3721000A3B21 /* AVFileQuery.m in Sources */, 70CA7F841BDE3721000A3B21 /* LCFileTaskManager.m in Sources */, 70CA7F851BDE3721000A3B21 /* AVGeoPoint.m in Sources */, @@ -3573,19 +3586,17 @@ 70CA7F881BDE3721000A3B21 /* AVObjectUtils.m in Sources */, 835092171EB1ECB7000DA884 /* AVDynamicObject.m in Sources */, 70CA7F891BDE3721000A3B21 /* AVRelation.m in Sources */, - 83FCB9DD1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */, 9A404DD81CE5A9C200DEB3DC /* LCRouter.m in Sources */, 70CA7F8A1BDE3721000A3B21 /* AVRequestManager.m in Sources */, - 83FCB9C91CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */, 70CA7F8B1BDE3721000A3B21 /* AVInstallation.m in Sources */, 70CA7F8C1BDE3721000A3B21 /* AVPush.m in Sources */, 70BA7A691BE2041200C3EB70 /* AVKeychain.m in Sources */, 8302AEDC1C192A0700E13F8A /* LCURLConnection.m in Sources */, + D37EE4B223ACF39700AACE99 /* LCSecurityPolicy.m in Sources */, 70CA7F8D1BDE3721000A3B21 /* AVCloudQueryResult.m in Sources */, - 83FCB9D31CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */, 70CA7F8E1BDE3721000A3B21 /* AVFriendQuery.m in Sources */, + D37EE4C223ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */, 70CA7F8F1BDE3721000A3B21 /* AVQuery.m in Sources */, - 83FCB9B51CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */, 70CA7F901BDE3721000A3B21 /* AVRequestOperation.m in Sources */, 70CA7F911BDE3721000A3B21 /* AVPaasClient.m in Sources */, 83331D4A1EB9A25F00CADC9C /* AVCaptcha.m in Sources */, @@ -3594,7 +3605,6 @@ 70CA7F941BDE3721000A3B21 /* LCNetworkStatistics.m in Sources */, 70CA7F951BDE3721000A3B21 /* AVStatus.m in Sources */, 835092021EB1BDD0000DA884 /* AVSMS.m in Sources */, - 83FCB9BF1CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3613,18 +3623,17 @@ 8320F88A1C1918C60024B45E /* AVSearchQuery.m in Sources */, 8320F88D1C1918C60024B45E /* LCDatabase.m in Sources */, 8320F88E1C1918C60024B45E /* NSDictionary+LCHash.m in Sources */, - 83FCB9D51CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */, 835092041EB1BDD0000DA884 /* AVSMS.m in Sources */, + D37EE4C423ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */, 8320F88F1C1918C60024B45E /* AVKeychain.m in Sources */, 8320F8901C1918C60024B45E /* AVQuery.m in Sources */, - 83FCB9DF1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */, 8320F8961C1918C60024B45E /* AVPersistenceUtils.m in Sources */, 83331D4C1EB9A25F00CADC9C /* AVCaptcha.m in Sources */, - 83FCB9C11CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */, 8320F8981C1918C60024B45E /* AVAnonymousUtils.m in Sources */, 8320F8991C1918C60024B45E /* AVUser.m in Sources */, 8320F89B1C1918C60024B45E /* AVInstallation.m in Sources */, 8320F89C1C1918C60024B45E /* LCDatabasePool.m in Sources */, + D37EE4B423ACF39700AACE99 /* LCSecurityPolicy.m in Sources */, 830EB9FD1C44D68400BA917F /* AVSaveOption.m in Sources */, 8320F89E1C1918C60024B45E /* AVOSCloud.m in Sources */, 8320F8A01C1918C60024B45E /* AVObjectUtils.m in Sources */, @@ -3638,18 +3647,17 @@ 8320F8B01C1918C60024B45E /* AVPaasClient.m in Sources */, 8302AEDE1C192A0700E13F8A /* LCURLConnection.m in Sources */, 8320F8B21C1918C60024B45E /* AVErrorUtils.m in Sources */, - 83FCB9CB1CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */, 8320F8B31C1918C60024B45E /* LCDatabaseMigrator.m in Sources */, 8320F8B61C1918C60024B45E /* LCResultSet.m in Sources */, 8320F8B81C1918C60024B45E /* AVStatus.m in Sources */, + D37EE4E423ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */, 8320F8BB1C1918C60024B45E /* AVGeoPoint.m in Sources */, 8320F8BC1C1918C60024B45E /* AVCacheManager.m in Sources */, 8320F8BD1C1918C60024B45E /* AVACL.m in Sources */, 8320F8BE1C1918C60024B45E /* AVFriendQuery.m in Sources */, 8320F8C01C1918C60024B45E /* AVPush.m in Sources */, - 83FCB9A81CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */, + D37EE4D423ACF39700AACE99 /* LCURLSessionManager.m in Sources */, 8320F8C11C1918C60024B45E /* LCDatabaseCoordinator.m in Sources */, - 83FCB9B71CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */, 8320F8C51C1918C60024B45E /* AVRelation.m in Sources */, 9A404DE01CE5A9C400DEB3DC /* LCRouter.m in Sources */, 83331D5B1EB9D55100CADC9C /* NSDictionary+LeanCloud.m in Sources */, @@ -3657,7 +3665,9 @@ 8320F8CA1C1918C60024B45E /* AVScheduler.m in Sources */, 8320F8CC1C1918C60024B45E /* AVCloudQueryResult.m in Sources */, 835092191EB1ECB7000DA884 /* AVDynamicObject.m in Sources */, + D37EE4D023ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */, 8320F8D01C1918C60024B45E /* LCKeyValueStore.m in Sources */, + D37EE4C023ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3694,9 +3704,8 @@ 83D8CE7B1C17DAF50094279D /* LCDatabaseAdditions.m in Sources */, 83D8CE7C1C17DAF50094279D /* AVUtils.m in Sources */, 83D8CE7E1C17DAF50094279D /* AVAnalyticsUtils.m in Sources */, + D37EE4CF23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */, 83D8CE831C17DAF50094279D /* AVSearchQuery.m in Sources */, - 83FCB9DE1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */, - 83FCB9D41CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */, 83331D4B1EB9A25F00CADC9C /* AVCaptcha.m in Sources */, 83D8CE861C17DAF50094279D /* LCDatabase.m in Sources */, 83D8CE871C17DAF50094279D /* NSDictionary+LCHash.m in Sources */, @@ -3709,6 +3718,7 @@ 83D8CE921C17DAF50094279D /* AVUser.m in Sources */, 83D8CE941C17DAF50094279D /* AVInstallation.m in Sources */, 83D8CE951C17DAF50094279D /* LCDatabasePool.m in Sources */, + D37EE4BF23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */, 83D8CE971C17DAF50094279D /* AVOSCloud.m in Sources */, 83D8CE991C17DAF50094279D /* AVObjectUtils.m in Sources */, 83D8CE9E1C17DAF50094279D /* AVCloud.m in Sources */, @@ -3719,32 +3729,32 @@ 83D8CEA51C17DAF50094279D /* LCNetworkStatistics.m in Sources */, 83D8CEA61C17DAF50094279D /* AVDuration.m in Sources */, 83D8CEA81C17DAF50094279D /* AVRole.m in Sources */, + D37EE4D323ACF39700AACE99 /* LCURLSessionManager.m in Sources */, + D37EE4E323ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */, 835092031EB1BDD0000DA884 /* AVSMS.m in Sources */, 83D8CEA91C17DAF50094279D /* AVPaasClient.m in Sources */, 8302AEDD1C192A0700E13F8A /* LCURLConnection.m in Sources */, 83D8CEAB1C17DAF50094279D /* AVErrorUtils.m in Sources */, 83D8CEAC1C17DAF50094279D /* LCDatabaseMigrator.m in Sources */, - 83FCB9A71CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */, 83D8CEAF1C17DAF50094279D /* LCResultSet.m in Sources */, 83331D5A1EB9D55100CADC9C /* NSDictionary+LeanCloud.m in Sources */, - 83FCB9CA1CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */, 83D8CEB11C17DAF50094279D /* AVStatus.m in Sources */, 83D8CEB41C17DAF50094279D /* AVGeoPoint.m in Sources */, 83D8CEB51C17DAF50094279D /* AVCacheManager.m in Sources */, 83D8CEB61C17DAF50094279D /* AVACL.m in Sources */, 83D8CEB71C17DAF50094279D /* AVFriendQuery.m in Sources */, 83D8CEB91C17DAF50094279D /* AVPush.m in Sources */, + D37EE4B323ACF39700AACE99 /* LCSecurityPolicy.m in Sources */, 835092181EB1ECB7000DA884 /* AVDynamicObject.m in Sources */, 83D8CEBA1C17DAF50094279D /* LCDatabaseCoordinator.m in Sources */, + D37EE4C323ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */, 83D8CEBE1C17DAF50094279D /* AVRelation.m in Sources */, 83D8CEBF1C17DAF50094279D /* AVReachability.m in Sources */, - 83FCB9B61CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */, 9A404DDC1CE5A9C300DEB3DC /* LCRouter.m in Sources */, 83D8CEC11C17DAF50094279D /* AVLogger.m in Sources */, 83D8CEC31C17DAF50094279D /* AVScheduler.m in Sources */, 83D8CEC51C17DAF50094279D /* AVCloudQueryResult.m in Sources */, 83D8CEC61C17DAF50094279D /* AVAnalytics.m in Sources */, - 83FCB9C01CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */, 83D8CEC91C17DAF50094279D /* LCKeyValueStore.m in Sources */, 830EB9FC1C44D68400BA917F /* AVSaveOption.m in Sources */, ); @@ -3762,9 +3772,8 @@ 83DF02B31AF86032000E289C /* LCDatabaseAdditions.m in Sources */, 8C841B651A5A7A0000C5C6C4 /* AVUtils.m in Sources */, 8C841ABD1A5A7A0000C5C6C4 /* AVAnalyticsUtils.m in Sources */, + D37EE4CD23ACF39700AACE99 /* LCHTTPSessionManager.m in Sources */, 8C841B371A5A7A0000C5C6C4 /* AVSearchQuery.m in Sources */, - 83FCB9DB1CEDC57C007D8712 /* LCURLSessionManager.m in Sources */, - 83FCB9D11CEDC57C007D8712 /* LCURLResponseSerialization.m in Sources */, 83331D491EB9A25F00CADC9C /* AVCaptcha.m in Sources */, 83DF02B11AF86032000E289C /* LCDatabase.m in Sources */, 837CC71F1B568431001333AD /* NSDictionary+LCHash.m in Sources */, @@ -3777,6 +3786,7 @@ 8C841B5A1A5A7A0000C5C6C4 /* AVUser.m in Sources */, 8C841B221A5A7A0000C5C6C4 /* AVInstallation.m in Sources */, 83DF02B51AF86032000E289C /* LCDatabasePool.m in Sources */, + D37EE4BD23ACF39700AACE99 /* LCURLRequestSerialization.m in Sources */, 8C841AC71A5A7A0000C5C6C4 /* AVOSCloud.m in Sources */, 8C841B1C1A5A7A0000C5C6C4 /* AVObjectUtils.m in Sources */, 8C841ACF1A5A7A0000C5C6C4 /* AVCloud.m in Sources */, @@ -3787,32 +3797,32 @@ 838DD7971B3D309F00C95897 /* LCNetworkStatistics.m in Sources */, 8C841ABF1A5A7A0000C5C6C4 /* AVDuration.m in Sources */, 8C841AB11A5A7A0000C5C6C4 /* AVRole.m in Sources */, + D37EE4D123ACF39700AACE99 /* LCURLSessionManager.m in Sources */, + D37EE4E123ACF39700AACE99 /* LCNetworkReachabilityManager.m in Sources */, 835092011EB1BDD0000DA884 /* AVSMS.m in Sources */, 8C841B331A5A7A0000C5C6C4 /* AVPaasClient.m in Sources */, 8302AEDA1C192A0700E13F8A /* LCURLConnection.m in Sources */, 8C841B5D1A5A7A0000C5C6C4 /* AVErrorUtils.m in Sources */, 830E7CFD1B1CAD3B005F4B22 /* LCDatabaseMigrator.m in Sources */, - 83FCB9A41CEDC57C007D8712 /* LCHTTPSessionManager.m in Sources */, 83DF02BA1AF86032000E289C /* LCResultSet.m in Sources */, 83331D581EB9D55100CADC9C /* NSDictionary+LeanCloud.m in Sources */, - 83FCB9C71CEDC57C007D8712 /* LCURLRequestSerialization.m in Sources */, 8C841B3B1A5A7A0000C5C6C4 /* AVStatus.m in Sources */, 8C841AE81A5A7A0000C5C6C4 /* AVGeoPoint.m in Sources */, 8C841AC91A5A7A0000C5C6C4 /* AVCacheManager.m in Sources */, 8C841AAE1A5A7A0000C5C6C4 /* AVACL.m in Sources */, 8C841B2B1A5A7A0000C5C6C4 /* AVFriendQuery.m in Sources */, 8C841B251A5A7A0000C5C6C4 /* AVPush.m in Sources */, + D37EE4B123ACF39700AACE99 /* LCSecurityPolicy.m in Sources */, 835092161EB1ECB7000DA884 /* AVDynamicObject.m in Sources */, 830E7CF91B1CA2A2005F4B22 /* LCDatabaseCoordinator.m in Sources */, + D37EE4C123ACF39700AACE99 /* LCURLResponseSerialization.m in Sources */, 8C841B1E1A5A7A0000C5C6C4 /* AVRelation.m in Sources */, 8C841B631A5A7A0000C5C6C4 /* AVReachability.m in Sources */, - 83FCB9B31CEDC57C007D8712 /* LCNetworkReachabilityManager.m in Sources */, 83F9A2C81CE014430002E21B /* LCRouter.m in Sources */, 8C841B611A5A7A0000C5C6C4 /* AVLogger.m in Sources */, 8C841ACD1A5A7A0000C5C6C4 /* AVScheduler.m in Sources */, 8C841B281A5A7A0000C5C6C4 /* AVCloudQueryResult.m in Sources */, 8C841AB41A5A7A0000C5C6C4 /* AVAnalytics.m in Sources */, - 83FCB9BD1CEDC57C007D8712 /* LCSecurityPolicy.m in Sources */, 838DD7A11B3D3FB600C95897 /* LCKeyValueStore.m in Sources */, 830EB9F91C44D68400BA917F /* AVSaveOption.m in Sources */, ); diff --git a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m index 80300d5ac..694aaa547 100644 --- a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m +++ b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m @@ -164,14 +164,14 @@ +(NSString *)connectionType [reachabilityManager startMonitoring]; switch (reachabilityManager.networkReachabilityStatus) { - case AFNetworkReachabilityStatusUnknown: - break; - case AFNetworkReachabilityStatusNotReachable: - break; - case AFNetworkReachabilityStatusReachableViaWiFi: - type = WiFiType; - case AFNetworkReachabilityStatusReachableViaWWAN: - type = WWANType; + case LCNetworkReachabilityStatusUnknown: + break; + case LCNetworkReachabilityStatusNotReachable: + break; + case LCNetworkReachabilityStatusReachableViaWiFi: + type = WiFiType; + case LCNetworkReachabilityStatusReachableViaWWAN: + type = WWANType; } return type; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h new file mode 100644 index 000000000..516c1bb53 --- /dev/null +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h @@ -0,0 +1,37 @@ +// LCCompatibilityMacros.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef LCCompatibilityMacros_h +#define LCCompatibilityMacros_h + +#ifdef API_UNAVAILABLE + #define LC_API_UNAVAILABLE(x) API_UNAVAILABLE(x) +#else + #define LC_API_UNAVAILABLE(x) +#endif // API_UNAVAILABLE + +#if __has_warning("-Wunguarded-availability-new") + #define LC_CAN_USE_AT_AVAILABLE 1 +#else + #define LC_CAN_USE_AT_AVAILABLE 0 +#endif + +#endif /* LCCompatibilityMacros_h */ diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h old mode 100755 new mode 100644 index 7b7eb8a4a..72d91cf8d --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h @@ -1,4 +1,4 @@ -// AFHTTPSessionManager.h +// LCHTTPSessionManager.h // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -34,23 +34,23 @@ #import "LCURLSessionManager.h" /** - `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. + `LCHTTPSessionManager` is a subclass of `LCURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. ## Subclassing Notes - Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `LCHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. - For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `LCHTTPRequestOperationManager` may be used to similar effect. ## Methods to Override - To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. + To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:`. ## Serialization - Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. - Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` ## URL Construction Using Relative Paths @@ -81,30 +81,39 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, nonatomic, strong, nullable) NSURL *baseURL; /** - Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `LCHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. @warning `requestSerializer` must not be `nil`. */ @property (nonatomic, strong) LCHTTPRequestSerializer * requestSerializer; /** - Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `LCJSONResponseSerializer`. @warning `responseSerializer` must not be `nil`. */ @property (nonatomic, strong) LCHTTPResponseSerializer * responseSerializer; +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created session to evaluate server trust for secure connections. `LCURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `LCSSLPinningModePublicKey` or `LCSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception. + */ +@property (nonatomic, strong) LCSecurityPolicy *securityPolicy; + ///--------------------- /// @name Initialization ///--------------------- /** - Creates and returns an `AFHTTPSessionManager` object. + Creates and returns an `LCHTTPSessionManager` object. */ + (instancetype)manager; /** - Initializes an `AFHTTPSessionManager` object with the specified base URL. + Initializes an `LCHTTPSessionManager` object with the specified base URL. @param url The base URL for the HTTP client. @@ -113,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithBaseURL:(nullable NSURL *)url; /** - Initializes an `AFHTTPSessionManager` object with the specified base URL. + Initializes an `LCHTTPSessionManager` object with the specified base URL. This is the designated initializer. @@ -214,7 +223,7 @@ NS_ASSUME_NONNULL_BEGIN @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `LCMultipartFormData` protocol. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @@ -231,7 +240,7 @@ NS_ASSUME_NONNULL_BEGIN @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `LCMultipartFormData` protocol. @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m old mode 100755 new mode 100644 index f69ffdf52..d856e9a9d --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m @@ -1,4 +1,4 @@ -// AFHTTPSessionManager.m +// LCHTTPSessionManager.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -98,6 +98,23 @@ - (void)setResponseSerializer:(LCHTTPResponseSerializer -typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { - AFNetworkReachabilityStatusUnknown = -1, - AFNetworkReachabilityStatusNotReachable = 0, - AFNetworkReachabilityStatusReachableViaWWAN = 1, - AFNetworkReachabilityStatusReachableViaWiFi = 2, +typedef NS_ENUM(NSInteger, LCNetworkReachabilityStatus) { + LCNetworkReachabilityStatusUnknown = -1, + LCNetworkReachabilityStatusNotReachable = 0, + LCNetworkReachabilityStatusReachableViaWWAN = 1, + LCNetworkReachabilityStatusReachableViaWiFi = 2, }; NS_ASSUME_NONNULL_BEGIN /** - `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + `LCNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ ) - @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. + @warning Instances of `LCNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. */ @interface LCNetworkReachabilityManager : NSObject /** The current network reachability status. */ -@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; +@property (readonly, nonatomic, assign) LCNetworkReachabilityStatus networkReachabilityStatus; /** Whether or not the network is currently reachable. @@ -64,6 +64,8 @@ NS_ASSUME_NONNULL_BEGIN */ @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; +@property (nonatomic, strong) dispatch_queue_t reachabilityQueue; + ///--------------------- /// @name Initialization ///--------------------- @@ -107,6 +109,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; +/** + * Unavailable initializer + */ ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Unavailable initializer + */ +- (instancetype)init NS_UNAVAILABLE; + ///-------------------------------------------------- /// @name Starting & Stopping Reachability Monitoring ///-------------------------------------------------- @@ -139,7 +151,7 @@ NS_ASSUME_NONNULL_BEGIN @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. */ -- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; +- (void)setReachabilityStatusChangeBlock:(nullable void (^)(LCNetworkReachabilityStatus status))block; @end @@ -150,34 +162,34 @@ NS_ASSUME_NONNULL_BEGIN /** ## Network Reachability - The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. + The following constants are provided by `LCNetworkReachabilityManager` as possible network reachability statuses. enum { - AFNetworkReachabilityStatusUnknown, - AFNetworkReachabilityStatusNotReachable, - AFNetworkReachabilityStatusReachableViaWWAN, - AFNetworkReachabilityStatusReachableViaWiFi, + LCNetworkReachabilityStatusUnknown, + LCNetworkReachabilityStatusNotReachable, + LCNetworkReachabilityStatusReachableViaWWAN, + LCNetworkReachabilityStatusReachableViaWiFi, } - `AFNetworkReachabilityStatusUnknown` + `LCNetworkReachabilityStatusUnknown` The `baseURL` host reachability is not known. - `AFNetworkReachabilityStatusNotReachable` + `LCNetworkReachabilityStatusNotReachable` The `baseURL` host cannot be reached. - `AFNetworkReachabilityStatusReachableViaWWAN` + `LCNetworkReachabilityStatusReachableViaWWAN` The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. - `AFNetworkReachabilityStatusReachableViaWiFi` + `LCNetworkReachabilityStatusReachableViaWiFi` The `baseURL` host can be reached via a Wi-Fi connection. ### Keys for Notification UserInfo Dictionary Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. - `AFNetworkingReachabilityNotificationStatusItem` - A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. - The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. + `LCNetworkingReachabilityNotificationStatusItem` + A key in the userInfo dictionary in a `LCNetworkingReachabilityDidChangeNotification` notification. + The corresponding value is an `NSNumber` object representing the `LCNetworkReachabilityStatus` value for the current reachability status. */ ///-------------------- @@ -186,7 +198,7 @@ NS_ASSUME_NONNULL_BEGIN /** Posted when network reachability changes. - This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. + This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `LCNetworkingReachabilityNotificationStatusItem` key, representing the `LCNetworkReachabilityStatus` value for the current network reachability. @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). */ @@ -198,9 +210,9 @@ FOUNDATION_EXPORT NSString * const LCNetworkingReachabilityNotificationStatusIte ///-------------------- /** - Returns a localized string representation of an `AFNetworkReachabilityStatus` value. + Returns a localized string representation of an `LCNetworkReachabilityStatus` value. */ -FOUNDATION_EXPORT NSString * LCStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); +FOUNDATION_EXPORT NSString * LCStringFromNetworkReachabilityStatus(LCNetworkReachabilityStatus status); NS_ASSUME_NONNULL_END #endif diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m old mode 100755 new mode 100644 index ccd46da47..e72ff6d35 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m @@ -1,4 +1,4 @@ -// AFNetworkReachabilityManager.m +// LCNetworkReachabilityManager.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,43 +28,43 @@ #import #import -NSString * const LCNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; +NSString * const LCNetworkingReachabilityDidChangeNotification = @"com.leancloud.networking.reachability.change"; NSString * const LCNetworkingReachabilityNotificationStatusItem = @"LCNetworkingReachabilityNotificationStatusItem"; -typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); +typedef void (^LCNetworkReachabilityStatusBlock)(LCNetworkReachabilityStatus status); -NSString * LCStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { +NSString * LCStringFromNetworkReachabilityStatus(LCNetworkReachabilityStatus status) { switch (status) { - case AFNetworkReachabilityStatusNotReachable: - return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); - case AFNetworkReachabilityStatusReachableViaWWAN: - return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); - case AFNetworkReachabilityStatusReachableViaWiFi: - return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); - case AFNetworkReachabilityStatusUnknown: + case LCNetworkReachabilityStatusNotReachable: + return NSLocalizedStringFromTable(@"Not Reachable", @"LCNetworking", nil); + case LCNetworkReachabilityStatusReachableViaWWAN: + return NSLocalizedStringFromTable(@"Reachable via WWAN", @"LCNetworking", nil); + case LCNetworkReachabilityStatusReachableViaWiFi: + return NSLocalizedStringFromTable(@"Reachable via WiFi", @"LCNetworking", nil); + case LCNetworkReachabilityStatusUnknown: default: - return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); + return NSLocalizedStringFromTable(@"Unknown", @"LCNetworking", nil); } } -static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { +static LCNetworkReachabilityStatus LCNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; + LCNetworkReachabilityStatus status = LCNetworkReachabilityStatusUnknown; if (isNetworkReachable == NO) { - status = AFNetworkReachabilityStatusNotReachable; + status = LCNetworkReachabilityStatusNotReachable; } #if TARGET_OS_IPHONE else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { - status = AFNetworkReachabilityStatusReachableViaWWAN; + status = LCNetworkReachabilityStatusReachableViaWWAN; } #endif else { - status = AFNetworkReachabilityStatusReachableViaWiFi; + status = LCNetworkReachabilityStatusReachableViaWiFi; } return status; @@ -78,28 +78,26 @@ static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetwork * a queued notification (for an earlier status condition) is processed after * the later update, resulting in the listener being left in the wrong state. */ -static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); - dispatch_async(dispatch_get_main_queue(), ^{ - if (block) { - block(status); - } - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - NSDictionary *userInfo = @{ LCNetworkingReachabilityNotificationStatusItem: @(status) }; - [notificationCenter postNotificationName:LCNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; - }); +static void LCPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, LCNetworkReachabilityStatusBlock block) { + LCNetworkReachabilityStatus status = LCNetworkReachabilityStatusForFlags(flags); + if (block) { + block(status); + } + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ LCNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:LCNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; } -static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { - AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); +static void LCNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + LCPostReachabilityStatusChange(flags, (__bridge LCNetworkReachabilityStatusBlock)info); } -static const void * AFNetworkReachabilityRetainCallback(const void *info) { +static const void * LCNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info); } -static void AFNetworkReachabilityReleaseCallback(const void *info) { +static void LCNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } @@ -107,8 +105,8 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { @interface LCNetworkReachabilityManager () @property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability; -@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; -@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; +@property (readwrite, nonatomic, assign) LCNetworkReachabilityStatus networkReachabilityStatus; +@property (readwrite, nonatomic, copy) LCNetworkReachabilityStatusBlock networkReachabilityStatusBlock; @end @implementation LCNetworkReachabilityManager @@ -165,13 +163,17 @@ - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { } _networkReachability = CFRetain(reachability); - self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + self.networkReachabilityStatus = LCNetworkReachabilityStatusUnknown; + self.reachabilityQueue = dispatch_queue_create("com.leancloud.reachability", DISPATCH_QUEUE_SERIAL); return self; } -- (instancetype)init NS_UNAVAILABLE +- (instancetype)init { + @throw [NSException exceptionWithName:NSGenericException + reason:@"`-init` unavailable. Use `-initWithReachability:` instead" + userInfo:nil]; return nil; } @@ -190,11 +192,11 @@ - (BOOL)isReachable { } - (BOOL)isReachableViaWWAN { - return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; + return self.networkReachabilityStatus == LCNetworkReachabilityStatusReachableViaWWAN; } - (BOOL)isReachableViaWiFi { - return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; + return self.networkReachabilityStatus == LCNetworkReachabilityStatusReachableViaWiFi; } #pragma mark - @@ -207,7 +209,7 @@ - (void)startMonitoring { } __weak __typeof(self)weakSelf = self; - AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { + LCNetworkReachabilityStatusBlock callback = ^(LCNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; @@ -217,14 +219,14 @@ - (void)startMonitoring { }; - SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; - SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); - SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ + SCNetworkReachabilityContext context = {0, (__bridge void *)callback, LCNetworkReachabilityRetainCallback, LCNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, LCNetworkReachabilityCallback, &context); + NSParameterAssert(self.reachabilityQueue); + SCNetworkReachabilitySetDispatchQueue(self.networkReachability, self.reachabilityQueue); + dispatch_async(self.reachabilityQueue,^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { - AFPostReachabilityStatusChange(flags, callback); + LCPostReachabilityStatusChange(flags, callback); } }); } @@ -234,7 +236,7 @@ - (void)stopMonitoring { return; } - SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + SCNetworkReachabilitySetDispatchQueue(self.networkReachability, NULL); } #pragma mark - @@ -245,7 +247,7 @@ - (NSString *)localizedNetworkReachabilityStatusString { #pragma mark - -- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { +- (void)setReachabilityStatusChangeBlock:(void (^)(LCNetworkReachabilityStatus status))block { self.networkReachabilityStatusBlock = block; } diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h old mode 100755 new mode 100644 index ff5cb6c34..dfb248572 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h @@ -1,4 +1,4 @@ -// AFNetworking.h +// LCNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com/) // @@ -24,8 +24,8 @@ #import #import -#ifndef _AFNETWORKING_ - #define _AFNETWORKING_ +#ifndef _LCNETWORKING_ + #define _LCNETWORKING_ #import "LCURLRequestSerialization.h" #import "LCURLResponseSerialization.h" @@ -38,4 +38,4 @@ #import "LCURLSessionManager.h" #import "LCHTTPSessionManager.h" -#endif /* _AFNETWORKING_ */ +#endif /* _LCNETWORKING_ */ diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h old mode 100755 new mode 100644 index 893fb3749..9a8fbde9c --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h @@ -1,4 +1,4 @@ -// AFSecurityPolicy.h +// LCSecurityPolicy.h // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,14 +22,14 @@ #import #import -typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { - AFSSLPinningModeNone, - AFSSLPinningModePublicKey, - AFSSLPinningModeCertificate, +typedef NS_ENUM(NSUInteger, LCSSLPinningMode) { + LCSSLPinningModeNone, + LCSSLPinningModePublicKey, + LCSSLPinningModeCertificate, }; /** - `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + `LCSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. */ @@ -39,9 +39,9 @@ NS_ASSUME_NONNULL_BEGIN @interface LCSecurityPolicy : NSObject /** - The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. + The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `LCSSLPinningModeNone`. */ -@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readonly, nonatomic, assign) LCSSLPinningMode SSLPinningMode; /** The certificates used to evaluate server trust according to the SSL pinning mode. @@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN @return A new security policy. */ -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; ++ (instancetype)policyWithPinningMode:(LCSSLPinningMode)pinningMode; /** Creates and returns a security policy with the specified pinning mode. @@ -105,7 +105,7 @@ NS_ASSUME_NONNULL_BEGIN @return A new security policy. */ -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates; ++ (instancetype)policyWithPinningMode:(LCSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates; ///------------------------------ /// @name Evaluating Server Trust @@ -135,20 +135,20 @@ NS_ASSUME_NONNULL_END /** ## SSL Pinning Modes - The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. + The following constants are provided by `LCSSLPinningMode` as possible SSL pinning modes. enum { - AFSSLPinningModeNone, - AFSSLPinningModePublicKey, - AFSSLPinningModeCertificate, + LCSSLPinningModeNone, + LCSSLPinningModePublicKey, + LCSSLPinningModeCertificate, } - `AFSSLPinningModeNone` + `LCSSLPinningModeNone` Do not used pinned certificates to validate servers. - `AFSSLPinningModePublicKey` + `LCSSLPinningModePublicKey` Validate host certificates against public keys of pinned certificates. - `AFSSLPinningModeCertificate` + `LCSSLPinningModeCertificate` Validate host certificates against pinned certificates. */ diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m old mode 100755 new mode 100644 index a66c77071..a5f3ab387 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m @@ -1,4 +1,4 @@ -// AFSecurityPolicy.m +// LCSecurityPolicy.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,7 +24,7 @@ #import #if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV -static NSData * AFSecKeyGetData(SecKeyRef key) { +static NSData * LCSecKeyGetData(SecKeyRef key) { CFDataRef data = NULL; __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); @@ -40,19 +40,17 @@ } #endif -static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { +static BOOL LCSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { #if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV return [(__bridge id)key1 isEqual:(__bridge id)key2]; #else - return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; + return [LCSecKeyGetData(key1) isEqual:LCSecKeyGetData(key2)]; #endif } -static id AFPublicKeyForCertificate(NSData *certificate) { +static id LCPublicKeyForCertificate(NSData *certificate) { id allowedPublicKey = nil; SecCertificateRef allowedCertificate; - SecCertificateRef allowedCertificates[1]; - CFArrayRef tempCertificates = nil; SecPolicyRef policy = nil; SecTrustRef allowedTrust = nil; SecTrustResultType result; @@ -60,11 +58,8 @@ static id AFPublicKeyForCertificate(NSData *certificate) { allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); __Require_Quiet(allowedCertificate != NULL, _out); - allowedCertificates[0] = allowedCertificate; - tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); - policy = SecPolicyCreateBasicX509(); - __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out); __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); @@ -78,10 +73,6 @@ static id AFPublicKeyForCertificate(NSData *certificate) { CFRelease(policy); } - if (tempCertificates) { - CFRelease(tempCertificates); - } - if (allowedCertificate) { CFRelease(allowedCertificate); } @@ -89,7 +80,7 @@ static id AFPublicKeyForCertificate(NSData *certificate) { return allowedPublicKey; } -static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { +static BOOL LCServerTrustIsValid(SecTrustRef serverTrust) { BOOL isValid = NO; SecTrustResultType result; __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); @@ -100,7 +91,7 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { return isValid; } -static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { +static NSArray * LCCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; @@ -112,7 +103,7 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { return [NSArray arrayWithArray:trustChain]; } -static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { +static NSArray * LCPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { SecPolicyRef policy = SecPolicyCreateBasicX509(); CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; @@ -149,7 +140,7 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { #pragma mark - @interface LCSecurityPolicy() -@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readwrite, nonatomic, assign) LCSSLPinningMode SSLPinningMode; @property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys; @end @@ -180,16 +171,16 @@ + (NSSet *)defaultPinnedCertificates { + (instancetype)defaultPolicy { LCSecurityPolicy *securityPolicy = [[self alloc] init]; - securityPolicy.SSLPinningMode = AFSSLPinningModeNone; + securityPolicy.SSLPinningMode = LCSSLPinningModeNone; return securityPolicy; } -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { ++ (instancetype)policyWithPinningMode:(LCSSLPinningMode)pinningMode { return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; } -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { ++ (instancetype)policyWithPinningMode:(LCSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { LCSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; @@ -215,7 +206,7 @@ - (void)setPinnedCertificates:(NSSet *)pinnedCertificates { if (self.pinnedCertificates) { NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; for (NSData *certificate in self.pinnedCertificates) { - id publicKey = AFPublicKeyForCertificate(certificate); + id publicKey = LCPublicKeyForCertificate(certificate); if (!publicKey) { continue; } @@ -232,7 +223,7 @@ - (void)setPinnedCertificates:(NSSet *)pinnedCertificates { - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { - if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { + if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == LCSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html // According to the docs, you should only trust your provided certs for evaluation. // Pinned certificates are added to the trust. Without pinned certificates, @@ -254,29 +245,29 @@ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); - if (self.SSLPinningMode == AFSSLPinningModeNone) { - return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); - } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + if (self.SSLPinningMode == LCSSLPinningModeNone) { + return self.allowInvalidCertificates || LCServerTrustIsValid(serverTrust); + } else if (!LCServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { return NO; } switch (self.SSLPinningMode) { - case AFSSLPinningModeNone: + case LCSSLPinningModeNone: default: return NO; - case AFSSLPinningModeCertificate: { + case LCSSLPinningModeCertificate: { NSMutableArray *pinnedCertificates = [NSMutableArray array]; for (NSData *certificateData in self.pinnedCertificates) { [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; } SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); - if (!AFServerTrustIsValid(serverTrust)) { + if (!LCServerTrustIsValid(serverTrust)) { return NO; } // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA) - NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); + NSArray *serverCertificates = LCCertificateTrustChainForServerTrust(serverTrust); for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { if ([self.pinnedCertificates containsObject:trustChainCertificate]) { @@ -286,13 +277,13 @@ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust return NO; } - case AFSSLPinningModePublicKey: { + case LCSSLPinningModePublicKey: { NSUInteger trustedPublicKeyCount = 0; - NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); + NSArray *publicKeys = LCPublicKeyTrustChainForServerTrust(serverTrust); for (id trustChainPublicKey in publicKeys) { for (id pinnedPublicKey in self.pinnedPublicKeys) { - if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { + if (LCSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; } } diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h old mode 100755 new mode 100644 index 6a5aea0f0..3aec92d00 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h @@ -1,4 +1,4 @@ -// AFURLRequestSerialization.h +// LCURLRequestSerialization.h // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -56,7 +56,7 @@ FOUNDATION_EXPORT NSString * LCPercentEscapedStringFromString(NSString *string); FOUNDATION_EXPORT NSString * LCQueryStringFromParameters(NSDictionary *parameters); /** - The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. + The `LCURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. */ @@ -82,16 +82,16 @@ FOUNDATION_EXPORT NSString * LCQueryStringFromParameters(NSDictionary *parameter /** */ -typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { - AFHTTPRequestQueryStringDefaultStyle = 0, +typedef NS_ENUM(NSUInteger, LCHTTPRequestQueryStringSerializationStyle) { + LCHTTPRequestQueryStringDefaultStyle = 0, }; @protocol LCMultipartFormData; /** - `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + `LCHTTPRequestSerializer` conforms to the `LCURLRequestSerialization` & `LCURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. - Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. + Any request or response serializer dealing with HTTP is encouraged to subclass `LCHTTPRequestSerializer` in order to ensure consistent default behavior. */ @interface LCHTTPRequestSerializer : NSObject @@ -207,9 +207,9 @@ forHTTPHeaderField:(NSString *)field; @param style The serialization style. - @see AFHTTPRequestQueryStringSerializationStyle + @see LCHTTPRequestQueryStringSerializationStyle */ -- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; +- (void)setQueryStringSerializationWithStyle:(LCHTTPRequestQueryStringSerializationStyle)style; /** Set the a custom method of query string serialization according to the specified block. @@ -247,7 +247,7 @@ forHTTPHeaderField:(NSString *)field; @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded and set in the request HTTP body. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `LCMultipartFormData` protocol. @param error The error that occurred while constructing the request. @return An `NSMutableURLRequest` object @@ -265,7 +265,7 @@ forHTTPHeaderField:(NSString *)field; @param fileURL The file URL to write multipart form contents to. @param handler A handler block to execute. - @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. + @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `LCURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. @see https://github.com/AFNetworking/AFNetworking/issues/1398 */ @@ -278,7 +278,7 @@ forHTTPHeaderField:(NSString *)field; #pragma mark - /** - The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. + The `LCMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `LCHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. */ @protocol LCMultipartFormData @@ -365,7 +365,7 @@ forHTTPHeaderField:(NSString *)field; /** Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. - When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. + When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kLCUploadStream3GSuggestedPacketSize` and `kLCUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. @param delay Duration of delay each time a packet is read. By default, no delay is set. @@ -378,7 +378,7 @@ forHTTPHeaderField:(NSString *)field; #pragma mark - /** - `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. + `LCJSONRequestSerializer` is a subclass of `LCHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. */ @interface LCJSONRequestSerializer : LCHTTPRequestSerializer @@ -399,7 +399,7 @@ forHTTPHeaderField:(NSString *)field; #pragma mark - /** - `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. + `LCPropertyListRequestSerializer` is a subclass of `LCHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. */ @interface LCPropertyListRequestSerializer : LCHTTPRequestSerializer @@ -437,12 +437,12 @@ forHTTPHeaderField:(NSString *)field; The following error domain is predefined. - - `NSString * const AFURLRequestSerializationErrorDomain` + - `NSString * const LCURLRequestSerializationErrorDomain` ### Constants - `AFURLRequestSerializationErrorDomain` - AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + `LCURLRequestSerializationErrorDomain` + LCURLRequestSerializer errors. Error codes for `LCURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. */ FOUNDATION_EXPORT NSString * const LCURLRequestSerializationErrorDomain; @@ -451,12 +451,12 @@ FOUNDATION_EXPORT NSString * const LCURLRequestSerializationErrorDomain; These keys may exist in the user info dictionary, in addition to those defined for NSError. - - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` + - `NSString * const LCNetworkingOperationFailingURLRequestErrorKey` ### Constants - `AFNetworkingOperationFailingURLRequestErrorKey` - The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`. + `LCNetworkingOperationFailingURLRequestErrorKey` + The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `LCURLRequestSerializationErrorDomain`. */ FOUNDATION_EXPORT NSString * const LCNetworkingOperationFailingURLRequestErrorKey; @@ -467,10 +467,10 @@ FOUNDATION_EXPORT NSString * const LCNetworkingOperationFailingURLRequestErrorKe ### Constants - `kAFUploadStream3GSuggestedPacketSize` + `kLCUploadStream3GSuggestedPacketSize` Maximum packet size, in number of bytes. Equal to 16kb. - `kAFUploadStream3GSuggestedDelay` + `kLCUploadStream3GSuggestedDelay` Duration of delay each time a packet is read. Equal to 0.2 seconds. */ FOUNDATION_EXPORT NSUInteger const kLCUploadStream3GSuggestedPacketSize; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m old mode 100755 new mode 100644 index a87e58334..03cce1db7 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m @@ -1,4 +1,4 @@ -// AFURLRequestSerialization.m +// LCURLRequestSerialization.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,10 +27,10 @@ #import #endif -NSString * const LCURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; -NSString * const LCNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; +NSString * const LCURLRequestSerializationErrorDomain = @"com.leancloud.error.serialization.request"; +NSString * const LCNetworkingOperationFailingURLRequestErrorKey = @"com.leancloud.serialization.request.error.response"; -typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); +typedef NSString * (^LCQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); /** Returns a percent-escaped string following RFC 3986 for a query string key or value. @@ -45,11 +45,11 @@ - returns: The percent-escaped string. */ NSString * LCPercentEscapedStringFromString(NSString *string) { - static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 - static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; + static NSString * const kLCCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 + static NSString * const kLCCharactersSubDelimitersToEncode = @"!$&'()*+,;="; NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; - [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; + [allowedCharacterSet removeCharactersInString:[kLCCharactersGeneralDelimitersToEncode stringByAppendingString:kLCCharactersSubDelimitersToEncode]]; // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; @@ -60,10 +60,7 @@ NSMutableString *escaped = @"".mutableCopy; while (index < string.length) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wgnu" NSUInteger length = MIN(string.length - index, batchSize); -#pragma GCC diagnostic pop NSRange range = NSMakeRange(index, length); // To avoid breaking up character sequences such as 👴🏻👮🏽 @@ -174,23 +171,24 @@ - (NSMutableURLRequest *)requestByFinalizingMultipartFormData; #pragma mark - -static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { - static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; +static NSArray * LCHTTPRequestSerializerObservedKeyPaths() { + static NSArray *_LCHTTPRequestSerializerObservedKeyPaths = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; + _LCHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; }); - return _AFHTTPRequestSerializerObservedKeyPaths; + return _LCHTTPRequestSerializerObservedKeyPaths; } -static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; +static void *LCHTTPRequestSerializerObserverContext = &LCHTTPRequestSerializerObserverContext; @interface LCHTTPRequestSerializer () @property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; -@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; -@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; +@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue; +@property (readwrite, nonatomic, assign) LCHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; +@property (readwrite, nonatomic, copy) LCQueryStringSerializationBlock queryStringSerialization; @end @implementation LCHTTPRequestSerializer @@ -208,6 +206,7 @@ - (instancetype)init { self.stringEncoding = NSUTF8StringEncoding; self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; + self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT); // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; @@ -219,8 +218,6 @@ - (instancetype)init { [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; NSString *userAgent = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" #if TARGET_OS_IOS // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; @@ -230,7 +227,6 @@ - (instancetype)init { #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; #endif -#pragma clang diagnostic pop if (userAgent) { if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { NSMutableString *mutableUserAgent = [userAgent mutableCopy]; @@ -245,9 +241,9 @@ - (instancetype)init { self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; self.mutableObservedChangedKeyPaths = [NSMutableSet set]; - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + for (NSString *keyPath in LCHTTPRequestSerializerObservedKeyPaths()) { if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { - [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:LCHTTPRequestSerializerObserverContext]; } } @@ -255,9 +251,9 @@ - (instancetype)init { } - (void)dealloc { - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + for (NSString *keyPath in LCHTTPRequestSerializerObservedKeyPaths()) { if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { - [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; + [self removeObserver:self forKeyPath:keyPath context:LCHTTPRequestSerializerObserverContext]; } } } @@ -306,17 +302,27 @@ - (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { #pragma mark - - (NSDictionary *)HTTPRequestHeaders { - return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; + NSDictionary __block *value; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; + }); + return value; } - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { - [self.mutableHTTPRequestHeaders setValue:value forKey:field]; + dispatch_barrier_async(self.requestHeaderModificationQueue, ^{ + [self.mutableHTTPRequestHeaders setValue:value forKey:field]; + }); } - (NSString *)valueForHTTPHeaderField:(NSString *)field { - return [self.mutableHTTPRequestHeaders valueForKey:field]; + NSString __block *value; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + value = [self.mutableHTTPRequestHeaders valueForKey:field]; + }); + return value; } - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username @@ -328,12 +334,14 @@ - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username } - (void)clearAuthorizationHeader { - [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; + dispatch_barrier_async(self.requestHeaderModificationQueue, ^{ + [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; + }); } #pragma mark - -- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { +- (void)setQueryStringSerializationWithStyle:(LCHTTPRequestQueryStringSerializationStyle)style { self.queryStringSerializationStyle = style; self.queryStringSerialization = nil; } @@ -359,7 +367,7 @@ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; mutableRequest.HTTPMethod = method; - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + for (NSString *keyPath in LCHTTPRequestSerializerObservedKeyPaths()) { if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; } @@ -461,7 +469,7 @@ - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request return mutableRequest; } -#pragma mark - AFURLRequestSerialization +#pragma mark - LCURLRequestSerialization - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters @@ -492,7 +500,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request } } else { switch (self.queryStringSerializationStyle) { - case AFHTTPRequestQueryStringDefaultStyle: + case LCHTTPRequestQueryStringDefaultStyle: query = LCQueryStringFromParameters(parameters); break; } @@ -520,7 +528,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request #pragma mark - NSKeyValueObserving + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { - if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { + if ([LCHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { return NO; } @@ -532,7 +540,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath change:(NSDictionary *)change context:(void *)context { - if (context == AFHTTPRequestSerializerObserverContext) { + if (context == LCHTTPRequestSerializerObserverContext) { if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { [self.mutableObservedChangedKeyPaths removeObject:keyPath]; } else { @@ -554,13 +562,15 @@ - (instancetype)initWithCoder:(NSCoder *)decoder { } self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; - self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; + self.queryStringSerializationStyle = (LCHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; + }); [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; } @@ -568,7 +578,9 @@ - (void)encodeWithCoder:(NSCoder *)coder { - (instancetype)copyWithZone:(NSZone *)zone { LCHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; + }); serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; serializer.queryStringSerialization = self.queryStringSerialization; @@ -579,25 +591,25 @@ - (instancetype)copyWithZone:(NSZone *)zone { #pragma mark - -static NSString * AFCreateMultipartFormBoundary() { +static NSString * LCCreateMultipartFormBoundary() { return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; } -static NSString * const kAFMultipartFormCRLF = @"\r\n"; +static NSString * const kLCMultipartFormCRLF = @"\r\n"; -static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; +static inline NSString * LCMultipartFormInitialBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"--%@%@", boundary, kLCMultipartFormCRLF]; } -static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +static inline NSString * LCMultipartFormEncapsulationBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@%@", kLCMultipartFormCRLF, boundary, kLCMultipartFormCRLF]; } -static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +static inline NSString * LCMultipartFormFinalBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@--%@", kLCMultipartFormCRLF, boundary, kLCMultipartFormCRLF]; } -static inline NSString * AFContentTypeForPathExtension(NSString *extension) { +static inline NSString * LCContentTypeForPathExtension(NSString *extension) { NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); if (!contentType) { @@ -661,12 +673,17 @@ - (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest self.request = urlRequest; self.stringEncoding = encoding; - self.boundary = AFCreateMultipartFormBoundary(); + self.boundary = LCCreateMultipartFormBoundary(); self.bodyStream = [[LCMultipartBodyStream alloc] initWithStringEncoding:encoding]; return self; } +- (void)setRequest:(NSMutableURLRequest *)request +{ + _request = [request mutableCopy]; +} + - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * __autoreleasing *)error @@ -675,7 +692,7 @@ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL NSParameterAssert(name); NSString *fileName = [fileURL lastPathComponent]; - NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); + NSString *mimeType = LCContentTypeForPathExtension([fileURL pathExtension]); return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; } @@ -692,14 +709,14 @@ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL NSParameterAssert(mimeType); if (![fileURL isFileURL]) { - NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)}; + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"LCNetworking", nil)}; if (error) { *error = [[NSError alloc] initWithDomain:LCURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; } return NO; } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { - NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)}; + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"LCNetworking", nil)}; if (error) { *error = [[NSError alloc] initWithDomain:LCURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; } @@ -835,14 +852,11 @@ @interface LCMultipartBodyStream () @end @implementation LCMultipartBodyStream -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wimplicit-atomic-properties" #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) @synthesize delegate; #endif @synthesize streamStatus; @synthesize streamError; -#pragma clang diagnostic pop - (instancetype)initWithStringEncoding:(NSStringEncoding)encoding { self = [super init]; @@ -888,8 +902,6 @@ - (NSInteger)read:(uint8_t *)buffer NSInteger totalNumberOfBytesRead = 0; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { @@ -910,7 +922,6 @@ - (NSInteger)read:(uint8_t *)buffer } } } -#pragma clang diagnostic pop return totalNumberOfBytesRead; } @@ -1004,14 +1015,14 @@ - (instancetype)copyWithZone:(NSZone *)zone { #pragma mark - typedef enum { - AFEncapsulationBoundaryPhase = 1, - AFHeaderPhase = 2, - AFBodyPhase = 3, - AFFinalBoundaryPhase = 4, -} AFHTTPBodyPartReadPhase; + LCEncapsulationBoundaryPhase = 1, + LCHeaderPhase = 2, + LCBodyPhase = 3, + LCFinalBoundaryPhase = 4, +} LCHTTPBodyPartReadPhase; @interface LCHTTPBodyPart () { - AFHTTPBodyPartReadPhase _phase; + LCHTTPBodyPartReadPhase _phase; NSInputStream *_inputStream; unsigned long long _phaseReadOffset; } @@ -1061,9 +1072,9 @@ - (NSInputStream *)inputStream { - (NSString *)stringForHeaders { NSMutableString *headerString = [NSMutableString string]; for (NSString *field in [self.headers allKeys]) { - [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; + [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kLCMultipartFormCRLF]]; } - [headerString appendString:kAFMultipartFormCRLF]; + [headerString appendString:kLCMultipartFormCRLF]; return [NSString stringWithString:headerString]; } @@ -1071,7 +1082,7 @@ - (NSString *)stringForHeaders { - (unsigned long long)contentLength { unsigned long long length = 0; - NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? LCMultipartFormInitialBoundary(self.boundary) : LCMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; length += [encapsulationBoundaryData length]; NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; @@ -1079,20 +1090,18 @@ - (unsigned long long)contentLength { length += _bodyContentLength; - NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [LCMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); length += [closingBoundaryData length]; return length; } - (BOOL)hasBytesAvailable { - // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer - if (_phase == AFFinalBoundaryPhase) { + // Allows `read:maxLength:` to be called again if `LCMultipartFormFinalBoundary` doesn't fit into the available buffer + if (_phase == LCFinalBoundaryPhase) { return YES; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" switch (self.inputStream.streamStatus) { case NSStreamStatusNotOpen: case NSStreamStatusOpening: @@ -1106,7 +1115,6 @@ - (BOOL)hasBytesAvailable { default: return NO; } -#pragma clang diagnostic pop } - (NSInteger)read:(uint8_t *)buffer @@ -1114,17 +1122,17 @@ - (NSInteger)read:(uint8_t *)buffer { NSInteger totalNumberOfBytesRead = 0; - if (_phase == AFEncapsulationBoundaryPhase) { - NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + if (_phase == LCEncapsulationBoundaryPhase) { + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? LCMultipartFormInitialBoundary(self.boundary) : LCMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } - if (_phase == AFHeaderPhase) { + if (_phase == LCHeaderPhase) { NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } - if (_phase == AFBodyPhase) { + if (_phase == LCBodyPhase) { NSInteger numberOfBytesRead = 0; numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; @@ -1139,8 +1147,8 @@ - (NSInteger)read:(uint8_t *)buffer } } - if (_phase == AFFinalBoundaryPhase) { - NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + if (_phase == LCFinalBoundaryPhase) { + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [LCMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } @@ -1151,11 +1159,8 @@ - (NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)length { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); [data getBytes:buffer range:range]; -#pragma clang diagnostic pop _phaseReadOffset += range.length; @@ -1174,28 +1179,25 @@ - (BOOL)transitionToNextPhase { return YES; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" switch (_phase) { - case AFEncapsulationBoundaryPhase: - _phase = AFHeaderPhase; + case LCEncapsulationBoundaryPhase: + _phase = LCHeaderPhase; break; - case AFHeaderPhase: + case LCHeaderPhase: [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [self.inputStream open]; - _phase = AFBodyPhase; + _phase = LCBodyPhase; break; - case AFBodyPhase: + case LCBodyPhase: [self.inputStream close]; - _phase = AFFinalBoundaryPhase; + _phase = LCFinalBoundaryPhase; break; - case AFFinalBoundaryPhase: + case LCFinalBoundaryPhase: default: - _phase = AFEncapsulationBoundaryPhase; + _phase = LCEncapsulationBoundaryPhase; break; } _phaseReadOffset = 0; -#pragma clang diagnostic pop return YES; } @@ -1232,7 +1234,7 @@ + (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOption return serializer; } -#pragma mark - AFURLRequestSerialization +#pragma mark - LCURLRequestSerialization - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters @@ -1257,7 +1259,21 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; } - [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; + if (![NSJSONSerialization isValidJSONObject:parameters]) { + if (error) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"LCNetworking", nil)}; + *error = [[NSError alloc] initWithDomain:LCURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; + } + return nil; + } + + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]; + + if (!jsonData) { + return nil; + } + + [mutableRequest setHTTPBody:jsonData]; } return mutableRequest; @@ -1311,7 +1327,7 @@ + (instancetype)serializerWithFormat:(NSPropertyListFormat)format return serializer; } -#pragma mark - AFURLRequestSerializer +#pragma mark - LCURLRequestSerializer - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters @@ -1336,7 +1352,13 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; } - [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; + NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]; + + if (!plistData) { + return nil; + } + + [mutableRequest setHTTPBody:plistData]; } return mutableRequest; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h old mode 100755 new mode 100644 index b4a8c1f64..42a74cc7a --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h @@ -1,4 +1,4 @@ -// AFURLResponseSerialization.h +// LCURLResponseSerialization.h // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN /** - The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. + The `LCURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. */ @@ -49,18 +49,15 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - /** - `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + `LCHTTPResponseSerializer` conforms to the `LCURLRequestSerialization` & `LCURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. - Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. + Any request or response serializer dealing with HTTP is encouraged to subclass `LCHTTPResponseSerializer` in order to ensure consistent default behavior. */ @interface LCHTTPResponseSerializer : NSObject - (instancetype)init; -/** - The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. - */ -@property (nonatomic, assign) NSStringEncoding stringEncoding; +@property (nonatomic, assign) NSStringEncoding stringEncoding DEPRECATED_MSG_ATTRIBUTE("The string encoding is never used. LCHTTPResponseSerializer only validates status codes and content types but does not try to decode the received data in any way."); /** Creates and returns a serializer with default configuration. @@ -104,13 +101,15 @@ NS_ASSUME_NONNULL_BEGIN /** - `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. + `LCJSONResponseSerializer` is a subclass of `LCHTTPResponseSerializer` that validates and decodes JSON responses. - By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: + By default, `LCJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: - `application/json` - `text/json` - `text/javascript` + + In RFC 7159 - Section 8.1, it states that JSON text is required to be encoded in UTF-8, UTF-16, or UTF-32, and the default encoding is UTF-8. NSJSONSerialization provides support for all the encodings listed in the specification, and recommends UTF-8 for efficiency. Using an unsupported encoding will result in serialization error. See the `NSJSONSerialization` documentation for more details. */ @interface LCJSONResponseSerializer : LCHTTPResponseSerializer @@ -138,9 +137,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - /** - `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. + `LCXMLParserResponseSerializer` is a subclass of `LCHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. - By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + By default, `LCXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` @@ -154,9 +153,9 @@ NS_ASSUME_NONNULL_BEGIN #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED /** - `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + `LCXMLDocumentResponseSerializer` is a subclass of `LCHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + By default, `LCXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` @@ -166,7 +165,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init; /** - Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSXMLDocument` documentation section "Input and Output Options". `0` by default. */ @property (nonatomic, assign) NSUInteger options; @@ -184,9 +183,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - /** - `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + `LCPropertyListResponseSerializer` is a subclass of `LCHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - By default, `AFPropertyListResponseSerializer` accepts the following MIME types: + By default, `LCPropertyListResponseSerializer` accepts the following MIME types: - `application/x-plist` */ @@ -218,9 +217,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - /** - `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. + `LCImageResponseSerializer` is a subclass of `LCHTTPResponseSerializer` that validates and decodes image responses. - By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: + By default, `LCImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: - `image/tiff` - `image/jpeg` @@ -252,7 +251,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - /** - `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. + `LCCompoundSerializer` is a subclass of `LCHTTPResponseSerializer` that delegates the response serialization to the first `LCHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `LCHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. */ @interface LCCompoundResponseSerializer : LCHTTPResponseSerializer @@ -264,7 +263,7 @@ NS_ASSUME_NONNULL_BEGIN /** Creates and returns a compound serializer comprised of the specified response serializers. - @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. + @warning Each response serializer specified must be a subclass of `LCHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. */ + (instancetype)compoundSerializerWithResponseSerializers:(NSArray > *)responseSerializers; @@ -279,12 +278,12 @@ NS_ASSUME_NONNULL_BEGIN The following error domain is predefined. - - `NSString * const AFURLResponseSerializationErrorDomain` + - `NSString * const LCURLResponseSerializationErrorDomain` ### Constants - `AFURLResponseSerializationErrorDomain` - AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + `LCURLResponseSerializationErrorDomain` + LCURLResponseSerializer errors. Error codes for `LCURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. */ FOUNDATION_EXPORT NSString * const LCURLResponseSerializationErrorDomain; @@ -293,16 +292,16 @@ FOUNDATION_EXPORT NSString * const LCURLResponseSerializationErrorDomain; These keys may exist in the user info dictionary, in addition to those defined for NSError. - - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` - - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` + - `NSString * const LCNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const LCNetworkingOperationFailingURLResponseDataErrorKey` ### Constants - `AFNetworkingOperationFailingURLResponseErrorKey` - The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + `LCNetworkingOperationFailingURLResponseErrorKey` + The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `LCURLResponseSerializationErrorDomain`. - `AFNetworkingOperationFailingURLResponseDataErrorKey` - The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + `LCNetworkingOperationFailingURLResponseDataErrorKey` + The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `LCURLResponseSerializationErrorDomain`. */ FOUNDATION_EXPORT NSString * const LCNetworkingOperationFailingURLResponseErrorKey; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m index 456508833..1a1978474 100755 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m @@ -1,4 +1,4 @@ -// AFURLResponseSerialization.m +// LCURLResponseSerialization.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -31,11 +31,11 @@ #import #endif -NSString * const LCURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; -NSString * const LCNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; -NSString * const LCNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; +NSString * const LCURLResponseSerializationErrorDomain = @"com.leancloud.error.serialization.response"; +NSString * const LCNetworkingOperationFailingURLResponseErrorKey = @"com.leancloud.serialization.response.error.response"; +NSString * const LCNetworkingOperationFailingURLResponseDataErrorKey = @"com.leancloud.serialization.response.error.data"; -static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { +static NSError * LCErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { if (!error) { return underlyingError; } @@ -50,21 +50,21 @@ return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; } -static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { +static BOOL LCErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { if ([error.domain isEqualToString:domain] && error.code == code) { return YES; } else if (error.userInfo[NSUnderlyingErrorKey]) { - return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); + return LCErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); } return NO; } -static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { +static id LCJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { if ([JSONObject isKindOfClass:[NSArray class]]) { NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; for (id value in (NSArray *)JSONObject) { - [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; + [mutableArray addObject:LCJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; } return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; @@ -75,7 +75,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO if (!value || [value isEqual:[NSNull null]]) { [mutableDictionary removeObjectForKey:key]; } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { - mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); + mutableDictionary[key] = LCJSONObjectByRemovingKeysWithNullValues(value, readingOptions); } } @@ -97,8 +97,6 @@ - (instancetype)init { return nil; } - self.stringEncoding = NSUTF8StringEncoding; - self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.acceptableContentTypes = nil; @@ -120,7 +118,7 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response if ([data length] > 0 && [response URL]) { NSMutableDictionary *mutableUserInfo = [@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"LCNetworking", nil), [response MIMEType]], NSURLErrorFailingURLErrorKey:[response URL], LCNetworkingOperationFailingURLResponseErrorKey: response, } mutableCopy]; @@ -128,7 +126,7 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response mutableUserInfo[LCNetworkingOperationFailingURLResponseDataErrorKey] = data; } - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:LCURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); + validationError = LCErrorWithUnderlyingError([NSError errorWithDomain:LCURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); } responseIsValid = NO; @@ -136,7 +134,7 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { NSMutableDictionary *mutableUserInfo = [@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"LCNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], NSURLErrorFailingURLErrorKey:[response URL], LCNetworkingOperationFailingURLResponseErrorKey: response, } mutableCopy]; @@ -145,7 +143,7 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response mutableUserInfo[LCNetworkingOperationFailingURLResponseDataErrorKey] = data; } - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:LCURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); + validationError = LCErrorWithUnderlyingError([NSError errorWithDomain:LCURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); responseIsValid = NO; } @@ -158,7 +156,7 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response return responseIsValid; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data @@ -230,35 +228,40 @@ - (instancetype)init { return self; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { + if (!error || LCErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { return nil; } } - id responseObject = nil; - NSError *serializationError = nil; // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. // See https://github.com/rails/rails/issues/1742 BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]]; - if (data.length > 0 && !isSpace) { - responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; - } else { + + if (data.length == 0 || isSpace) { return nil; } + + NSError *serializationError = nil; + + id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; - if (self.removesKeysWithNullValues && responseObject) { - responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); + if (!responseObject) + { + if (error) { + *error = LCErrorWithUnderlyingError(serializationError, *error); + } + return nil; } - - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); + + if (self.removesKeysWithNullValues) { + return LCJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); } return responseObject; @@ -288,7 +291,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - LCJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + LCJSONResponseSerializer *serializer = [super copyWithZone:zone]; serializer.readingOptions = self.readingOptions; serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; @@ -318,14 +321,14 @@ - (instancetype)init { return self; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { + if (!error || LCErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { return nil; } } @@ -363,14 +366,14 @@ - (instancetype)init { return self; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { + if (!error || LCErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { return nil; } } @@ -378,10 +381,14 @@ - (id)responseObjectForResponse:(NSURLResponse *)response NSError *serializationError = nil; NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); + if (!document) + { + if (error) { + *error = LCErrorWithUnderlyingError(serializationError, *error); + } + return nil; } - + return document; } @@ -407,7 +414,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - LCXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + LCXMLDocumentResponseSerializer *serializer = [super copyWithZone:zone]; serializer.options = self.options; return serializer; @@ -446,27 +453,32 @@ - (instancetype)init { return self; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { + if (!error || LCErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { return nil; } } - id responseObject; - NSError *serializationError = nil; - - if (data) { - responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; + if (!data) { + return nil; } - - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); + + NSError *serializationError = nil; + + id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; + + if (!responseObject) + { + if (error) { + *error = LCErrorWithUnderlyingError(serializationError, *error); + } + return nil; } return responseObject; @@ -496,7 +508,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - LCPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + LCPropertyListResponseSerializer *serializer = [super copyWithZone:zone]; serializer.format = self.format; serializer.readOptions = self.readOptions; @@ -511,15 +523,15 @@ - (instancetype)copyWithZone:(NSZone *)zone { #import #import -@interface UIImage (AFNetworkingSafeImageLoading) -+ (UIImage *)af_safeImageWithData:(NSData *)data; +@interface UIImage (LCNetworkingSafeImageLoading) ++ (UIImage *)lc_safeImageWithData:(NSData *)data; @end static NSLock* imageLock = nil; -@implementation UIImage (AFNetworkingSafeImageLoading) +@implementation UIImage (LCNetworkingSafeImageLoading) -+ (UIImage *)af_safeImageWithData:(NSData *)data { ++ (UIImage *)lc_safeImageWithData:(NSData *)data { UIImage* image = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -534,8 +546,8 @@ + (UIImage *)af_safeImageWithData:(NSData *)data { @end -static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { - UIImage *image = [UIImage af_safeImageWithData:data]; +static UIImage * LCImageWithDataAtScale(NSData *data, CGFloat scale) { + UIImage *image = [UIImage lc_safeImageWithData:data]; if (image.images) { return image; } @@ -543,7 +555,7 @@ + (UIImage *)af_safeImageWithData:(NSData *)data { return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; } -static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { +static UIImage * LCInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { if (!data || [data length] == 0) { return nil; } @@ -560,7 +572,7 @@ + (UIImage *)af_safeImageWithData:(NSData *)data { CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); - // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale + // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to LCImageWithDataAtScale if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { CGImageRelease(imageRef); imageRef = NULL; @@ -570,7 +582,7 @@ + (UIImage *)af_safeImageWithData:(NSData *)data { CGDataProviderRelease(dataProvider); - UIImage *image = AFImageWithDataAtScale(data, scale); + UIImage *image = LCImageWithDataAtScale(data, scale); if (!imageRef) { if (image.images || !image) { return image; @@ -658,23 +670,23 @@ - (instancetype)init { return self; } -#pragma mark - AFURLResponseSerializer +#pragma mark - LCURLResponseSerializer - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { + if (!error || LCErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, LCURLResponseSerializationErrorDomain)) { return nil; } } #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH if (self.automaticallyInflatesResponseImage) { - return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); + return LCInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); } else { - return AFImageWithDataAtScale(data, self.imageScale); + return LCImageWithDataAtScale(data, self.imageScale); } #else // Ensure that the image is set to it's correct pixel width and height @@ -722,7 +734,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - LCImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + LCImageResponseSerializer *serializer = [super copyWithZone:zone]; #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH serializer.imageScale = self.imageScale; @@ -749,7 +761,7 @@ + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSer return serializer; } -#pragma mark - AFURLResponseSerialization +#pragma mark - LCURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data @@ -764,7 +776,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; if (responseObject) { if (error) { - *error = AFErrorWithUnderlyingError(serializerError, *error); + *error = LCErrorWithUnderlyingError(serializerError, *error); } return responseObject; @@ -796,7 +808,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - LCCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + LCCompoundResponseSerializer *serializer = [super copyWithZone:zone]; serializer.responseSerializers = self.responseSerializers; return serializer; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h old mode 100755 new mode 100644 index 42678c922..40e8e6f0e --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h @@ -1,4 +1,4 @@ -// AFURLSessionManager.h +// LCURLSessionManager.h // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,20 +25,21 @@ #import "LCURLResponseSerialization.h" #import "LCURLRequestSerialization.h" #import "LCSecurityPolicy.h" +#import "LCCompatibilityMacros.h" #if !TARGET_OS_WATCH #import "LCNetworkReachabilityManager.h" #endif /** - `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + `LCURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. ## Subclassing Notes - This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. + This is the base class for `LCHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `LCURLSessionManager` specifically for HTTP, consider subclassing `LCHTTPSessionManager` instead. ## NSURLSession & NSURLSessionTask Delegate Methods - `AFURLSessionManager` implements the following delegate methods: + `LCURLSessionManager` implements the following delegate methods: ### `NSURLSessionDelegate` @@ -71,7 +72,7 @@ ## Network Reachability Monitoring - Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `LCNetworkReachabilityManager` for more details. ## NSCoding Caveats @@ -100,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; /** - Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `LCJSONResponseSerializer`. @warning `responseSerializer` must not be `nil`. */ @@ -111,7 +112,7 @@ NS_ASSUME_NONNULL_BEGIN ///------------------------------- /** - The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. + The security policy used by created session to evaluate server trust for secure connections. `LCURLSessionManager` uses the `defaultPolicy` unless otherwise specified. */ @property (nonatomic, strong) LCSecurityPolicy *securityPolicy; @@ -121,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN ///-------------------------------------- /** - The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. + The network reachability manager. `LCURLSessionManager` uses the `sharedManager` by default. */ @property (readwrite, nonatomic, strong) LCNetworkReachabilityManager *reachabilityManager; #endif @@ -208,7 +209,7 @@ NS_ASSUME_NONNULL_BEGIN @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE; /** Creates an `NSURLSessionDataTask` with the specified request. @@ -354,7 +355,7 @@ NS_ASSUME_NONNULL_BEGIN @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. */ -- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; +- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; /** Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. @@ -414,7 +415,7 @@ NS_ASSUME_NONNULL_BEGIN @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. */ -- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block; +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block LC_API_UNAVAILABLE(macos); ///----------------------------------------------- /// @name Setting Download Task Delegate Callbacks @@ -423,7 +424,7 @@ NS_ASSUME_NONNULL_BEGIN /** Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. - @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. + @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `LCURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. */ - (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; @@ -473,27 +474,27 @@ FOUNDATION_EXPORT NSString * const LCURLSessionDidInvalidateNotification; FOUNDATION_EXPORT NSString * const LCURLSessionDownloadTaskDidFailToMoveFileNotification; /** - The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task. + The raw response data of the task. Included in the userInfo dictionary of the `LCNetworkingTaskDidCompleteNotification` if response data exists for the task. */ FOUNDATION_EXPORT NSString * const LCNetworkingTaskDidCompleteResponseDataKey; /** - The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized. + The serialized response object of the task. Included in the userInfo dictionary of the `LCNetworkingTaskDidCompleteNotification` if the response was serialized. */ FOUNDATION_EXPORT NSString * const LCNetworkingTaskDidCompleteSerializedResponseKey; /** - The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer. + The response serializer used to serialize the response. Included in the userInfo dictionary of the `LCNetworkingTaskDidCompleteNotification` if the task has an associated response serializer. */ FOUNDATION_EXPORT NSString * const LCNetworkingTaskDidCompleteResponseSerializerKey; /** - The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk. + The file path associated with the download task. Included in the userInfo dictionary of the `LCNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk. */ FOUNDATION_EXPORT NSString * const LCNetworkingTaskDidCompleteAssetPathKey; /** - Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists. + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `LCNetworkingTaskDidCompleteNotification` if an error exists. */ FOUNDATION_EXPORT NSString * const LCNetworkingTaskDidCompleteErrorKey; diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m old mode 100755 new mode 100644 index 2444fd6e4..2df677e59 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m @@ -1,4 +1,4 @@ -// AFURLSessionManager.m +// LCURLSessionManager.m // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,7 +20,6 @@ // THE SOFTWARE. #import "LCURLSessionManager.h" -#import #ifndef NSFoundationVersionNumber_iOS_8_0 #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11 @@ -29,13 +28,13 @@ #endif static dispatch_queue_t url_session_manager_creation_queue() { - static dispatch_queue_t af_url_session_manager_creation_queue; + static dispatch_queue_t lc_url_session_manager_creation_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); + lc_url_session_manager_creation_queue = dispatch_queue_create("com.leancloud.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); }); - return af_url_session_manager_creation_queue; + return lc_url_session_manager_creation_queue; } static void url_session_manager_create_task_safely(dispatch_block_t block) { @@ -50,193 +49,132 @@ static void url_session_manager_create_task_safely(dispatch_block_t block) { } static dispatch_queue_t url_session_manager_processing_queue() { - static dispatch_queue_t af_url_session_manager_processing_queue; + static dispatch_queue_t lc_url_session_manager_processing_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); + lc_url_session_manager_processing_queue = dispatch_queue_create("com.leancloud.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); }); - return af_url_session_manager_processing_queue; + return lc_url_session_manager_processing_queue; } static dispatch_group_t url_session_manager_completion_group() { - static dispatch_group_t af_url_session_manager_completion_group; + static dispatch_group_t lc_url_session_manager_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - af_url_session_manager_completion_group = dispatch_group_create(); + lc_url_session_manager_completion_group = dispatch_group_create(); }); - return af_url_session_manager_completion_group; + return lc_url_session_manager_completion_group; } -NSString * const LCNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; -NSString * const LCNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; -NSString * const LCNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; -NSString * const LCURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; -NSString * const LCURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; +NSString * const LCNetworkingTaskDidResumeNotification = @"com.leancloud.networking.task.resume"; +NSString * const LCNetworkingTaskDidCompleteNotification = @"com.leancloud.networking.task.complete"; +NSString * const LCNetworkingTaskDidSuspendNotification = @"com.leancloud.networking.task.suspend"; +NSString * const LCURLSessionDidInvalidateNotification = @"com.leancloud.networking.session.invalidate"; +NSString * const LCURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.leancloud.networking.session.download.file-manager-error"; -NSString * const LCNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; -NSString * const LCNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; -NSString * const LCNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; -NSString * const LCNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; -NSString * const LCNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; +NSString * const LCNetworkingTaskDidCompleteSerializedResponseKey = @"com.leancloud.networking.task.complete.serializedresponse"; +NSString * const LCNetworkingTaskDidCompleteResponseSerializerKey = @"com.leancloud.networking.task.complete.responseserializer"; +NSString * const LCNetworkingTaskDidCompleteResponseDataKey = @"com.leancloud.networking.complete.finish.responsedata"; +NSString * const LCNetworkingTaskDidCompleteErrorKey = @"com.leancloud.networking.task.complete.error"; +NSString * const LCNetworkingTaskDidCompleteAssetPathKey = @"com.leancloud.networking.task.complete.assetpath"; -static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; +static NSString * const LCURLSessionManagerLockName = @"com.leancloud.networking.session.manager.lock"; -static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; +static NSUInteger const LCMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; -static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; +typedef void (^LCURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); +typedef NSURLSessionAuthChallengeDisposition (^LCURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); -typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); -typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef NSURLRequest * (^LCURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); +typedef NSURLSessionAuthChallengeDisposition (^LCURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef void (^LCURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); -typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); -typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); -typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); +typedef NSInputStream * (^LCURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); +typedef void (^LCURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); +typedef void (^LCURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); -typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); -typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); -typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); +typedef NSURLSessionResponseDisposition (^LCURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); +typedef void (^LCURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); +typedef void (^LCURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); +typedef NSCachedURLResponse * (^LCURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); -typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); -typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); -typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); -typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); +typedef NSURL * (^LCURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); +typedef void (^LCURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); +typedef void (^LCURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); +typedef void (^LCURLSessionTaskProgressBlock)(NSProgress *); -typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); -typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); -typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); -typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *); - -typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); +typedef void (^LCURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); #pragma mark - @interface LCURLSessionManagerTaskDelegate : NSObject +- (instancetype)initWithTask:(NSURLSessionTask *)task; @property (nonatomic, weak) LCURLSessionManager *manager; @property (nonatomic, strong) NSMutableData *mutableData; @property (nonatomic, strong) NSProgress *uploadProgress; @property (nonatomic, strong) NSProgress *downloadProgress; @property (nonatomic, copy) NSURL *downloadFileURL; -@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; -@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; -@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; -@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; +@property (nonatomic, copy) LCURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (nonatomic, copy) LCURLSessionTaskProgressBlock uploadProgressBlock; +@property (nonatomic, copy) LCURLSessionTaskProgressBlock downloadProgressBlock; +@property (nonatomic, copy) LCURLSessionTaskCompletionHandler completionHandler; @end @implementation LCURLSessionManagerTaskDelegate -- (instancetype)init { +- (instancetype)initWithTask:(NSURLSessionTask *)task { self = [super init]; if (!self) { return nil; } - - self.mutableData = [NSMutableData data]; - self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; - self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; - - self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; - self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; - return self; -} - -#pragma mark - NSProgress Tracking - -- (void)setupProgressForTask:(NSURLSessionTask *)task { + + _mutableData = [NSMutableData data]; + _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + __weak __typeof__(task) weakTask = task; - - self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; - self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive; - [self.uploadProgress setCancellable:YES]; - [self.uploadProgress setCancellationHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask cancel]; - }]; - [self.uploadProgress setPausable:YES]; - [self.uploadProgress setPausingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask suspend]; - }]; - if (@available(iOS 9.0, macOS 10.11, *)) { - if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) { - [self.uploadProgress setResumingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask resume]; - }]; - } - } - - [self.downloadProgress setCancellable:YES]; - [self.downloadProgress setCancellationHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask cancel]; - }]; - [self.downloadProgress setPausable:YES]; - [self.downloadProgress setPausingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask suspend]; - }]; - if (@available(iOS 9.0, macOS 10.11, *)) { - if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) { - [self.downloadProgress setResumingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask resume]; - }]; + for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) + { + progress.totalUnitCount = NSURLSessionTransferSizeUnknown; + progress.cancellable = YES; + progress.cancellationHandler = ^{ + [weakTask cancel]; + }; + progress.pausable = YES; + progress.pausingHandler = ^{ + [weakTask suspend]; + }; +#if LC_CAN_USE_AT_AVAILABLE + if (@available(iOS 9, macOS 10.11, *)) +#else + if ([progress respondsToSelector:@selector(setResumingHandler:)]) +#endif + { + progress.resumingHandler = ^{ + [weakTask resume]; + }; } + + [progress addObserver:self + forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) + options:NSKeyValueObservingOptionNew + context:NULL]; } + return self; +} - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) - options:NSKeyValueObservingOptionNew - context:NULL]; - - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) - options:NSKeyValueObservingOptionNew - context:NULL]; - - [self.downloadProgress addObserver:self - forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [self.uploadProgress addObserver:self - forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) - options:NSKeyValueObservingOptionNew - context:NULL]; -} - -- (void)cleanUpProgressForTask:(NSURLSessionTask *)task { - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]; +- (void)dealloc { [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; } +#pragma mark - NSProgress Tracking + - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) { - if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { - self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) { - self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { - self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) { - self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } - } - else if ([object isEqual:self.downloadProgress]) { + if ([object isEqual:self.downloadProgress]) { if (self.downloadProgressBlock) { self.downloadProgressBlock(object); } @@ -254,8 +192,6 @@ - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" __strong LCURLSessionManager *manager = self.manager; __block id responseObject = nil; @@ -317,33 +253,60 @@ - (void)URLSession:(__unused NSURLSession *)session }); }); } -#pragma clang diagnostic pop } -#pragma mark - NSURLSessionDataTaskDelegate +#pragma mark - NSURLSessionDataDelegate - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { + self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive; + self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived; + [self.mutableData appendData:data]; } -#pragma mark - NSURLSessionDownloadTaskDelegate +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ + + self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; + self.uploadProgress.completedUnitCount = task.countOfBytesSent; +} + +#pragma mark - NSURLSessionDownloadDelegate + +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ + + self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; + self.downloadProgress.completedUnitCount = totalBytesWritten; +} + +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes{ + + self.downloadProgress.totalUnitCount = expectedTotalBytes; + self.downloadProgress.completedUnitCount = fileOffset; +} - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { - NSError *fileManagerError = nil; self.downloadFileURL = nil; if (self.downloadTaskDidFinishDownloading) { self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (self.downloadFileURL) { - [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; + NSError *fileManagerError = nil; - if (fileManagerError) { + if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) { [[NSNotificationCenter defaultCenter] postNotificationName:LCURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } } @@ -354,130 +317,8 @@ - (void)URLSession:(NSURLSession *)session #pragma mark - -/** - * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. - * - * See: - * - https://github.com/AFNetworking/AFNetworking/issues/1477 - * - https://github.com/AFNetworking/AFNetworking/issues/2638 - * - https://github.com/AFNetworking/AFNetworking/pull/2702 - */ - -static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) { - Method originalMethod = class_getInstanceMethod(theClass, originalSelector); - Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); -} - -static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) { - return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method)); -} - -static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; -static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; - -@interface _LCURLSessionTaskSwizzling : NSObject - -@end - -@implementation _LCURLSessionTaskSwizzling - -+ (void)load { - /** - WARNING: Trouble Ahead - https://github.com/AFNetworking/AFNetworking/pull/2702 - */ - - if (NSClassFromString(@"NSURLSessionTask")) { - /** - iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. - Many Unit Tests have been built to validate as much of this behavior has possible. - Here is what we know: - - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. - - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. - - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. - - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. - - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. - - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. - - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. - - Some Assumptions: - - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. - - No background task classes override `resume` or `suspend` - - The current solution: - 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. - 2) Grab a pointer to the original implementation of `af_resume` - 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. - 4) Grab the super class of the current class. - 5) Grab a pointer for the current class to the current implementation of `resume`. - 6) Grab a pointer for the super class to the current implementation of `resume`. - 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods - 8) Set the current class to the super class, and repeat steps 3-8 - */ - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; - NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnonnull" - NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; -#pragma clang diagnostic pop - IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); - Class currentClass = [localDataTask class]; - - while (class_getInstanceMethod(currentClass, @selector(resume))) { - Class superClass = [currentClass superclass]; - IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); - IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); - if (classResumeIMP != superclassResumeIMP && - originalAFResumeIMP != classResumeIMP) { - [self swizzleResumeAndSuspendMethodForClass:currentClass]; - } - currentClass = [currentClass superclass]; - } - - [localDataTask cancel]; - [session finishTasksAndInvalidate]; - } -} - -+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { - Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); - Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); - - if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { - af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); - } - - if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { - af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend)); - } -} - -- (NSURLSessionTaskState)state { - NSAssert(NO, @"State method should never be called in the actual dummy class"); - return NSURLSessionTaskStateCanceling; -} - -- (void)af_resume { - NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); - NSURLSessionTaskState state = [self state]; - [self af_resume]; - - if (state != NSURLSessionTaskStateRunning) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; - } -} - -- (void)af_suspend { - NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); - NSURLSessionTaskState state = [self state]; - [self af_suspend]; - - if (state != NSURLSessionTaskStateSuspended) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; - } -} -@end +static NSString * const LCNSURLSessionTaskDidResumeNotification = @"com.leancloud.networking.nsurlsessiontask.resume"; +static NSString * const LCNSURLSessionTaskDidSuspendNotification = @"com.leancloud.networking.nsurlsessiontask.suspend"; #pragma mark - @@ -488,21 +329,21 @@ @interface LCURLSessionManager () @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; @property (readwrite, nonatomic, strong) NSLock *lock; -@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; -@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; -@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; -@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; -@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; +@property (readwrite, nonatomic, copy) LCURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; +@property (readwrite, nonatomic, copy) LCURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) LCURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession LC_API_UNAVAILABLE(macos); +@property (readwrite, nonatomic, copy) LCURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; +@property (readwrite, nonatomic, copy) LCURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) LCURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; +@property (readwrite, nonatomic, copy) LCURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; +@property (readwrite, nonatomic, copy) LCURLSessionTaskDidCompleteBlock taskDidComplete; +@property (readwrite, nonatomic, copy) LCURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; +@property (readwrite, nonatomic, copy) LCURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; +@property (readwrite, nonatomic, copy) LCURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; +@property (readwrite, nonatomic, copy) LCURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; +@property (readwrite, nonatomic, copy) LCURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (readwrite, nonatomic, copy) LCURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; +@property (readwrite, nonatomic, copy) LCURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; @end @implementation LCURLSessionManager @@ -539,7 +380,7 @@ - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; self.lock = [[NSLock alloc] init]; - self.lock.name = AFURLSessionManagerLockName; + self.lock.name = LCURLSessionManagerLockName; [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { for (NSURLSessionDataTask *task in dataTasks) { @@ -611,7 +452,6 @@ - (void)setDelegate:(LCURLSessionManagerTaskDelegate *)delegate [self.lock lock]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; - [delegate setupProgressForTask:task]; [self addNotificationObserverForTask:task]; [self.lock unlock]; } @@ -621,7 +461,7 @@ - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] init]; + LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] initWithTask:dataTask]; delegate.manager = self; delegate.completionHandler = completionHandler; @@ -636,7 +476,7 @@ - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] init]; + LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask]; delegate.manager = self; delegate.completionHandler = completionHandler; @@ -652,7 +492,7 @@ - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { - LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] init]; + LCURLSessionManagerTaskDelegate *delegate = [[LCURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask]; delegate.manager = self; delegate.completionHandler = completionHandler; @@ -672,9 +512,7 @@ - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask - (void)removeDelegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); - LCURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; [self.lock lock]; - [delegate cleanUpProgressForTask:task]; [self removeNotificationObserverForTask:task]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; @@ -723,13 +561,11 @@ - (NSArray *)downloadTasks { #pragma mark - - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { - dispatch_async(dispatch_get_main_queue(), ^{ - if (cancelPendingTasks) { - [self.session invalidateAndCancel]; - } else { - [self.session finishTasksAndInvalidate]; - } - }); + if (cancelPendingTasks) { + [self.session invalidateAndCancel]; + } else { + [self.session finishTasksAndInvalidate]; + } } #pragma mark - @@ -742,13 +578,13 @@ - (void)setResponseSerializer:(id )responseSerialize #pragma mark - - (void)addNotificationObserverForTask:(NSURLSessionTask *)task { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:LCNSURLSessionTaskDidResumeNotification object:task]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:LCNSURLSessionTaskDidSuspendNotification object:task]; } - (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:LCNSURLSessionTaskDidSuspendNotification object:task]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:LCNSURLSessionTaskDidResumeNotification object:task]; } #pragma mark - @@ -784,16 +620,21 @@ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request __block NSURLSessionUploadTask *uploadTask = nil; url_session_manager_create_task_safely(^{ uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; - }); - - if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { - for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { - uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + + // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113) + if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { + for (NSUInteger attempts = 0; !uploadTask && attempts < LCMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + } } + }); + + if (uploadTask) { + [self addDelegateForUploadTask:uploadTask + progress:uploadProgressBlock + completionHandler:completionHandler]; } - [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; - return uploadTask; } @@ -877,9 +718,11 @@ - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChalle self.sessionDidReceiveAuthenticationChallenge = block; } +#if !TARGET_OS_OSX - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { self.didFinishEventsForBackgroundURLSession = block; } +#endif #pragma mark - @@ -948,9 +791,12 @@ - (BOOL)respondsToSelector:(SEL)selector { return self.dataTaskDidReceiveResponse != nil; } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { return self.dataTaskWillCacheResponse != nil; - } else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { + } +#if !TARGET_OS_OSX + else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { return self.didFinishEventsForBackgroundURLSession != nil; } +#endif return [[self class] instancesRespondToSelector:selector]; } @@ -1076,6 +922,12 @@ - (void)URLSession:(NSURLSession *)session totalUnitCount = (int64_t) [contentLength longLongValue]; } } + + LCURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + + if (delegate) { + [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; + } if (self.taskDidSendBodyData) { self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); @@ -1162,8 +1014,7 @@ - (void)URLSession:(NSURLSession *)session } } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" +#if !TARGET_OS_OSX - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { if (self.didFinishEventsForBackgroundURLSession) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -1171,7 +1022,7 @@ - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session }); } } -#pragma clang diagnostic pop +#endif #pragma mark - NSURLSessionDownloadDelegate @@ -1185,8 +1036,8 @@ - (void)URLSession:(NSURLSession *)session if (fileURL) { delegate.downloadFileURL = fileURL; NSError *error = nil; - [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; - if (error) { + + if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) { [[NSNotificationCenter defaultCenter] postNotificationName:LCURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } @@ -1205,6 +1056,13 @@ - (void)URLSession:(NSURLSession *)session totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { + + LCURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + } + if (self.downloadTaskDidWriteData) { self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } @@ -1215,6 +1073,13 @@ - (void)URLSession:(NSURLSession *)session didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { + + LCURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; + } + if (self.downloadTaskDidResume) { self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); } diff --git a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m index 3d4457371..505449100 100644 --- a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m +++ b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m @@ -145,7 +145,7 @@ - (NSError *)decodingError:(AVIMGenericCommand *)command @interface AVIMWebSocketWrapper () @property (atomic, assign) BOOL isApplicationInBackground; -@property (atomic, assign) AFNetworkReachabilityStatus currentNetworkReachabilityStatus; +@property (atomic, assign) LCNetworkReachabilityStatus currentNetworkReachabilityStatus; @end @@ -235,16 +235,16 @@ - (instancetype)initWithDelegate:(id)delegate __weak typeof(self) weakSelf = self; self->_reachabilityMonitor = [LCNetworkReachabilityManager manager]; // this is not real init for Network Reachability Status, but need it to handle follow-up event. - self->_currentNetworkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + self->_currentNetworkReachabilityStatus = LCNetworkReachabilityStatusUnknown; self->_didInitNetworkReachabilityStatus = false; - [self->_reachabilityMonitor setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus newStatus) { + [self->_reachabilityMonitor setReachabilityStatusChangeBlock:^(LCNetworkReachabilityStatus newStatus) { AVIMWebSocketWrapper *strongSelf = weakSelf; if (!strongSelf) { return; } AVLoggerInfo(AVLoggerDomainIM, @" network reachability status: %@.", strongSelf, @(newStatus)); - AFNetworkReachabilityStatus oldStatus = strongSelf.currentNetworkReachabilityStatus; + LCNetworkReachabilityStatus oldStatus = strongSelf.currentNetworkReachabilityStatus; strongSelf.currentNetworkReachabilityStatus = newStatus; if (strongSelf->_didInitNetworkReachabilityStatus) { - if (oldStatus != AFNetworkReachabilityStatusNotReachable && newStatus == AFNetworkReachabilityStatusNotReachable) { + if (oldStatus != LCNetworkReachabilityStatusNotReachable && newStatus == LCNetworkReachabilityStatusNotReachable) { [strongSelf addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { NSError *error = ({ AVIMErrorCode code = AVIMErrorCodeConnectionLost; @@ -254,7 +254,7 @@ - (instancetype)initWithDelegate:(id)delegate [websocketWrapper purgeWithError:error]; [websocketWrapper pauseWithError:error]; }]; - } else if (oldStatus != newStatus && newStatus != AFNetworkReachabilityStatusNotReachable) { + } else if (oldStatus != newStatus && newStatus != LCNetworkReachabilityStatusNotReachable) { [strongSelf addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { NSError *error = ({ AVIMErrorCode code = AVIMErrorCodeConnectionLost; @@ -646,7 +646,7 @@ - (NSError *)checkIfCannotConnecting LCError(code, reason, nil); }); } - if (self.currentNetworkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) { + if (self.currentNetworkReachabilityStatus == LCNetworkReachabilityStatusNotReachable) { return ({ AVIMErrorCode code = AVIMErrorCodeConnectionLost; NSString *reason = @"Due to network unavailable, connection lost."; From 1b2a503e996183262aa4a78bb1cc641535d03297 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 23 Dec 2019 12:52:08 +0800 Subject: [PATCH 04/21] chore: error key and domain rollback for compatibility --- .../ThirdParty/LCNetworking/LCURLRequestSerialization.m | 4 ++-- .../ThirdParty/LCNetworking/LCURLResponseSerialization.m | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m index 03cce1db7..47414097d 100644 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m @@ -27,8 +27,8 @@ #import #endif -NSString * const LCURLRequestSerializationErrorDomain = @"com.leancloud.error.serialization.request"; -NSString * const LCNetworkingOperationFailingURLRequestErrorKey = @"com.leancloud.serialization.request.error.response"; +NSString * const LCURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; +NSString * const LCNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; typedef NSString * (^LCQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m index 1a1978474..8034ffa08 100755 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m @@ -31,9 +31,9 @@ #import #endif -NSString * const LCURLResponseSerializationErrorDomain = @"com.leancloud.error.serialization.response"; -NSString * const LCNetworkingOperationFailingURLResponseErrorKey = @"com.leancloud.serialization.response.error.response"; -NSString * const LCNetworkingOperationFailingURLResponseDataErrorKey = @"com.leancloud.serialization.response.error.data"; +NSString * const LCURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; +NSString * const LCNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; +NSString * const LCNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; static NSError * LCErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { if (!error) { From 344c70bbed4651a9a6671898315386b9dab2e2d0 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 23 Dec 2019 18:30:20 +0800 Subject: [PATCH 05/21] chore: remove unnecessary code --- AVOS/AVOSCloud/Analytics/AVAnalyticsImpl.m | 4 +-- AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.h | 1 - AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m | 31 ------------------- .../LCNetworking/LCURLSessionManager.h | 2 +- 4 files changed, 2 insertions(+), 36 deletions(-) diff --git a/AVOS/AVOSCloud/Analytics/AVAnalyticsImpl.m b/AVOS/AVOSCloud/Analytics/AVAnalyticsImpl.m index ee70e6505..aa39e2b53 100644 --- a/AVOS/AVOSCloud/Analytics/AVAnalyticsImpl.m +++ b/AVOS/AVOSCloud/Analytics/AVAnalyticsImpl.m @@ -386,9 +386,7 @@ -(void)postRecording { } else if (self.reportPolicy == AV_BATCH) { [self sendWithBatch]; } else if (self.reportPolicy == AV_SENDWIFIONLY) { - if ([AVAnalyticsUtils isWiFiConnection]) { - [self sendWithBatch]; - } + [self sendWithBatch]; } else if (self.reportPolicy == AV_SEND_INTERVAL) { [self sendWithInterval]; } diff --git a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.h b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.h index 49e278e19..8be02ca66 100644 --- a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.h +++ b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.h @@ -40,7 +40,6 @@ +(NSString *)safeString:(NSString *)string; -+(BOOL)isWiFiConnection AV_WATCH_UNAVAILABLE; +(NSString *)deviceId AV_WATCH_UNAVAILABLE AV_OSX_UNAVAILABLE; @end diff --git a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m index 694aaa547..d7098cd2a 100644 --- a/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m +++ b/AVOS/AVOSCloud/Analytics/AVAnalyticsUtils.m @@ -149,34 +149,6 @@ +(NSString *)language #if !TARGET_OS_WATCH -static NSString *const WiFiType = @"WiFi"; -static NSString *const WWANType = @"WWAN"; - -+(BOOL)isWiFiConnection { - return [[AVAnalyticsUtils connectionType] isEqualToString:WiFiType]; -} - -+(NSString *)connectionType -{ - NSString *type = @""; - LCNetworkReachabilityManager *reachabilityManager = [LCNetworkReachabilityManager sharedManager]; - - [reachabilityManager startMonitoring]; - - switch (reachabilityManager.networkReachabilityStatus) { - case LCNetworkReachabilityStatusUnknown: - break; - case LCNetworkReachabilityStatusNotReachable: - break; - case LCNetworkReachabilityStatusReachableViaWiFi: - type = WiFiType; - case LCNetworkReachabilityStatusReachableViaWWAN: - type = WWANType; - } - - return type; -} - +(NSString *)deviceId { static NSString * uniqueIdentifier = nil; @@ -302,9 +274,6 @@ +(NSMutableDictionary *)deviceInfo if (sdkType) { dynamicDic[@"os"] = sdkType; } #if !TARGET_OS_WATCH - NSString *connectionType = [AVAnalyticsUtils connectionType]; - if (connectionType) { dynamicDic[@"access"] = connectionType; } - NSString *carrier = [AVAnalyticsUtils carrier]; if (carrier) { dynamicDic[@"carrier"] = carrier; } #endif diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h index 40e8e6f0e..3072514a7 100644 --- a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h +++ b/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h @@ -209,7 +209,7 @@ NS_ASSUME_NONNULL_BEGIN @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE; + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; /** Creates an `NSURLSessionDataTask` with the specified request. From 867ffdca07649d30e79b1239adaf3ee21a8c8d96 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 23 Dec 2019 18:31:15 +0800 Subject: [PATCH 06/21] refactor(rtm): reachability of connection --- .../WebSocket/AVIMWebSocketWrapper.m | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m index 505449100..c103fcea6 100644 --- a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m +++ b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m @@ -234,8 +234,8 @@ - (instancetype)initWithDelegate:(id)delegate __weak typeof(self) weakSelf = self; self->_reachabilityMonitor = [LCNetworkReachabilityManager manager]; - // this is not real init for Network Reachability Status, but need it to handle follow-up event. - self->_currentNetworkReachabilityStatus = LCNetworkReachabilityStatusUnknown; + self->_reachabilityMonitor.reachabilityQueue = self->_internalSerialQueue; + self->_currentNetworkReachabilityStatus = self->_reachabilityMonitor.networkReachabilityStatus; self->_didInitNetworkReachabilityStatus = false; [self->_reachabilityMonitor setReachabilityStatusChangeBlock:^(LCNetworkReachabilityStatus newStatus) { AVIMWebSocketWrapper *strongSelf = weakSelf; @@ -245,26 +245,22 @@ - (instancetype)initWithDelegate:(id)delegate strongSelf.currentNetworkReachabilityStatus = newStatus; if (strongSelf->_didInitNetworkReachabilityStatus) { if (oldStatus != LCNetworkReachabilityStatusNotReachable && newStatus == LCNetworkReachabilityStatusNotReachable) { - [strongSelf addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { - NSError *error = ({ - AVIMErrorCode code = AVIMErrorCodeConnectionLost; - NSString *reason = @"Due to network unavailable, connection lost."; - LCError(code, reason, nil); - }); - [websocketWrapper purgeWithError:error]; - [websocketWrapper pauseWithError:error]; - }]; + NSError *error = ({ + AVIMErrorCode code = AVIMErrorCodeConnectionLost; + NSString *reason = @"Due to network unavailable, connection lost."; + LCError(code, reason, nil); + }); + [strongSelf purgeWithError:error]; + [strongSelf pauseWithError:error]; } else if (oldStatus != newStatus && newStatus != LCNetworkReachabilityStatusNotReachable) { - [strongSelf addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { - NSError *error = ({ - AVIMErrorCode code = AVIMErrorCodeConnectionLost; - NSString *reason = @"Due to network interface did change, connection lost."; - LCError(code, reason, nil); - }); - [websocketWrapper purgeWithError:error]; - [websocketWrapper pauseWithError:error]; - [websocketWrapper tryConnecting:false]; - }]; + NSError *error = ({ + AVIMErrorCode code = AVIMErrorCodeConnectionLost; + NSString *reason = @"Due to network interface did change, connection lost."; + LCError(code, reason, nil); + }); + [strongSelf purgeWithError:error]; + [strongSelf pauseWithError:error]; + [strongSelf tryConnecting:false]; } } else { strongSelf->_didInitNetworkReachabilityStatus = true; From 129f1c264d20e3510592a3dc68c11e91f20a40cd Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 23 Dec 2019 18:31:24 +0800 Subject: [PATCH 07/21] test: update case --- AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift b/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift index 8a2f304f3..504b7d70e 100644 --- a/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift +++ b/AVOS/AVOSCloudIMTests/Swift/AVIMMessageTestCase.swift @@ -1489,7 +1489,7 @@ class AVIMMessageTestCase: LCIMTestBase { XCTAssertTrue(commonMessage.sendTimestamp > 0) XCTAssertNotNil(commonMessage.clientId) if succeeded { - XCTAssertTrue(RunLoop.current.run(mode: RunLoop.Mode.default, before: Date(timeIntervalSinceNow: 1.0))) + sleep(1) normalConv.update(commonMessage, toNewMessage: newCommonMessage, callback: { (succeeded: Bool, error: Error?) in semaphore.decrement() XCTAssertTrue(Thread.isMainThread) @@ -1575,7 +1575,7 @@ class AVIMMessageTestCase: LCIMTestBase { XCTAssertTrue(commonMessage.sendTimestamp > 0) XCTAssertNotNil(commonMessage.clientId) if succeeded { - XCTAssertTrue(RunLoop.current.run(mode: RunLoop.Mode.default, before: Date(timeIntervalSinceNow: 1.0))) + sleep(1) normalConv.recall(commonMessage, callback: { (succeeded: Bool, error: Error?, recalledMessage: AVIMRecalledMessage?) in semaphore.decrement() XCTAssertTrue(Thread.isMainThread) From d988410180f819291e25d686a5a32a02a480a6ef Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 23 Dec 2019 19:23:51 +0800 Subject: [PATCH 08/21] chore: add jetfire --- AVOS/AVOS.xcodeproj/project.pbxproj | 24 + AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h | 82 ++ AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m | 218 ++++++ AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h | 159 ++++ AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 805 ++++++++++++++++++++ 5 files changed, 1288 insertions(+) create mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h create mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m create mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h create mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m diff --git a/AVOS/AVOS.xcodeproj/project.pbxproj b/AVOS/AVOS.xcodeproj/project.pbxproj index 5a8f56de6..99f217578 100644 --- a/AVOS/AVOS.xcodeproj/project.pbxproj +++ b/AVOS/AVOS.xcodeproj/project.pbxproj @@ -771,6 +771,14 @@ D31126A7208F1C7A00812135 /* AVIMConversationMemberInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A2208F1C7900812135 /* AVIMConversationMemberInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; D31126A8208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */; }; D31126A9208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */; }; + D3120E0B23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */; }; + D3120E0C23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */; }; + D3120E0D23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */; }; + D3120E0E23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */; }; + D3120E0F23B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */; }; + D3120E1023B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */; }; + D3120E1123B0DA8E00A64120 /* LCRTMSecurity.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */; }; + D3120E1223B0DA8E00A64120 /* LCRTMSecurity.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */; }; D31F503F21194D1100123909 /* AVIMMessageTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31F503E21194D1100123909 /* AVIMMessageTestCase.swift */; }; D328B8E020FC84930039091A /* LCIMTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CDF4EA1FE8F3810033153E /* LCIMTestBase.swift */; }; D328B8E220FC85200039091A /* LCLiveQueryTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D328B8E120FC85200039091A /* LCLiveQueryTestBase.swift */; }; @@ -1371,6 +1379,10 @@ D31126A1208F1C7900812135 /* AVIMConversationMemberInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVIMConversationMemberInfo.m; sourceTree = ""; }; D31126A2208F1C7900812135 /* AVIMConversationMemberInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMConversationMemberInfo.h; sourceTree = ""; }; D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMConversationMemberInfo_Internal.h; sourceTree = ""; }; + D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCRTMSecurity.h; sourceTree = ""; }; + D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCRTMWebSocket.h; sourceTree = ""; }; + D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCRTMWebSocket.m; sourceTree = ""; }; + D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCRTMSecurity.m; sourceTree = ""; }; D31F503E21194D1100123909 /* AVIMMessageTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVIMMessageTestCase.swift; sourceTree = ""; }; D328B8E120FC85200039091A /* LCLiveQueryTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCLiveQueryTestBase.swift; sourceTree = ""; }; D328B8E620FEE2200039091A /* AVIMClientInternalConversationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVIMClientInternalConversationManager.h; sourceTree = ""; }; @@ -2126,6 +2138,10 @@ 8C841C041A5A84C600C5C6C4 /* WebSocket */ = { isa = PBXGroup; children = ( + D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */, + D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */, + D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */, + D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */, 8C841C091A5A84C600C5C6C4 /* AVIMWebSocketWrapper.h */, 8C841C0A1A5A84C600C5C6C4 /* AVIMWebSocketWrapper.m */, ); @@ -2448,6 +2464,7 @@ D3D6E4E623544F590048E58F /* LCGPBType.pbobjc.h in Headers */, D3D6E4E223544F590048E58F /* LCGPBCodedOutputStream.h in Headers */, 704F3BF41BE0D0820033245C /* AVIMImageMessage.h in Headers */, + D3120E0C23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */, D3D6E4DA23544F590048E58F /* LCGPBDescriptor_PackagePrivate.h in Headers */, D3939CC820FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h in Headers */, 704F3BF51BE0D0820033245C /* AVIMLocationMessage.h in Headers */, @@ -2515,6 +2532,7 @@ D3D6E49E23544F590048E58F /* LCGPBUnknownFieldSet_PackagePrivate.h in Headers */, D3D6E4C423544F590048E58F /* LCGPBRootObject.h in Headers */, 704F3BFD1BE0D0830033245C /* AVIMErrorUtil.h in Headers */, + D3120E0E23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */, D3D6E4D023544F590048E58F /* LCGPBRuntimeTypes.h in Headers */, D3D6E4CE23544F590048E58F /* LCGPBCodedOutputStream_PackagePrivate.h in Headers */, D3D6E4A423544F590048E58F /* LCGPBRootObject_PackagePrivate.h in Headers */, @@ -2945,6 +2963,7 @@ D3D6E4E523544F590048E58F /* LCGPBType.pbobjc.h in Headers */, D3D6E4E123544F590048E58F /* LCGPBCodedOutputStream.h in Headers */, 8C9A92741A70AF1800CA4912 /* AVIMImageMessage.h in Headers */, + D3120E0B23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */, D3D6E4D923544F590048E58F /* LCGPBDescriptor_PackagePrivate.h in Headers */, 839A35671D2A1DC000B3AF1D /* AVIMMessage_Internal.h in Headers */, 9AD392461BFC411300D28074 /* AVIMGenericCommand+AVIMMessagesAdditions.h in Headers */, @@ -3012,6 +3031,7 @@ D3D6E49D23544F590048E58F /* LCGPBUnknownFieldSet_PackagePrivate.h in Headers */, D3D6E4C323544F590048E58F /* LCGPBRootObject.h in Headers */, 8C895DE61A78A5D900992A8F /* AVMPOrderedDictionary.h in Headers */, + D3120E0D23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */, D3D6E4CF23544F590048E58F /* LCGPBRuntimeTypes.h in Headers */, D3D6E4CD23544F590048E58F /* LCGPBCodedOutputStream_PackagePrivate.h in Headers */, D3D6E4A323544F590048E58F /* LCGPBRootObject_PackagePrivate.h in Headers */, @@ -3472,6 +3492,7 @@ files = ( D3D6E48223544F590048E58F /* LCGPBTimestamp.pbobjc.m in Sources */, 704F3C061BE0D5650033245C /* avmp.c in Sources */, + D3120E1223B0DA8E00A64120 /* LCRTMSecurity.m in Sources */, 704F3B851BE0D05C0033245C /* AVIMClient.m in Sources */, 704F3B861BE0D05C0033245C /* AVIMCommon.m in Sources */, D328B8EB20FEE2200039091A /* AVIMClientInternalConversationManager.m in Sources */, @@ -3526,6 +3547,7 @@ 704F3BB11BE0D05C0033245C /* AVIMLocationMessage.m in Sources */, 704F3BB21BE0D05C0033245C /* AVIMTextMessage.m in Sources */, D3D6E48823544F590048E58F /* LCGPBFieldMask.pbobjc.m in Sources */, + D3120E1023B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */, 704F3BB31BE0D05C0033245C /* AVIMTypedMessage.m in Sources */, 704F3BB41BE0D05C0033245C /* AVIMVideoMessage.m in Sources */, D3D6E4B423544F590048E58F /* LCGPBRootObject.m in Sources */, @@ -3850,6 +3872,7 @@ files = ( D3D6E48123544F590048E58F /* LCGPBTimestamp.pbobjc.m in Sources */, 83F745F21B91732D00437259 /* LCIMMessageCacheStore.m in Sources */, + D3120E1123B0DA8E00A64120 /* LCRTMSecurity.m in Sources */, D328B8EA20FEE2200039091A /* AVIMClientInternalConversationManager.m in Sources */, 8C2FED551A67C2DA0056F945 /* AVIMGeneralObject.m in Sources */, 8C9A92731A70AF1800CA4912 /* AVIMAudioMessage.m in Sources */, @@ -3904,6 +3927,7 @@ 833E1F641B69F629002A691C /* AVIMFileMessage.m in Sources */, 8C9A92751A70AF1800CA4912 /* AVIMImageMessage.m in Sources */, D3D6E48723544F590048E58F /* LCGPBFieldMask.pbobjc.m in Sources */, + D3120E0F23B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */, 9AD3924B1BFD7BE600D28074 /* MessagesProtoOrig.pbobjc.m in Sources */, 8C9A92771A70AF1800CA4912 /* AVIMLocationMessage.m in Sources */, D3D6E4B323544F590048E58F /* LCGPBRootObject.m in Sources */, diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h new file mode 100644 index 000000000..07278f1c3 --- /dev/null +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h @@ -0,0 +1,82 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LCRTMSecurity.h +// +// Created by Austin and Dalton Cherry on on 9/3/15. +// Copyright (c) 2014-2017 Austin Cherry. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#import +#import + +@interface LCRTMSSLCert : NSObject + +/** + Designated init for certificates + + :param: data is the binary data of the certificate + + :returns: a representation security object to be used with + */ +- (instancetype)initWithData:(NSData *)data; + + +/** + Designated init for public keys + + :param: key is the public key to be used + + :returns: a representation security object to be used with + */ +- (instancetype)initWithKey:(SecKeyRef)key; + +@end + +@interface LCRTMSecurity : NSObject + +/** + Use certs from main app bundle + + :param usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation + + :returns: a representation security object to be used with + */ +- (instancetype)initWithCerts:(NSArray*)certs publicKeys:(BOOL)publicKeys; + +/** + Designated init + + :param keys: is the certificates or public keys to use + :param usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation + + :returns: a representation security object to be used with + */ +- (instancetype)initUsingPublicKeys:(BOOL)publicKeys; + +/** + Should the domain name be validated? Default is YES. + */ +@property(nonatomic)BOOL validatedDN; + +/** + Validate if the cert is legit or not. + :param: trust is the trust to validate + :param: domain to validate along with the trust (can be nil) + :return: YES or NO if valid. + */ +- (BOOL)isValid:(SecTrustRef)trust domain:(NSString*)domain; + +@end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m new file mode 100644 index 000000000..98dcaa606 --- /dev/null +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m @@ -0,0 +1,218 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LCRTMSecurity.m +// +// Created by Austin and Dalton Cherry on on 9/3/15. +// Copyright (c) 2014-2017 Austin Cherry. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#import "LCRTMSecurity.h" + +@interface LCRTMSSLCert () + +@property(nonatomic, strong)NSData *certData; +@property(nonatomic)SecKeyRef key; + +@end + +@implementation LCRTMSSLCert + +///////////////////////////////////////////////////////////////////////////// +- (instancetype)initWithData:(NSData *)data { + if(self = [super init]) { + self.certData = data; + } + return self; +} +//////////////////////////////////////////////////////////////////////////// +- (instancetype)initWithKey:(SecKeyRef)key { + if(self = [super init]) { + self.key = key; + } + return self; +} +//////////////////////////////////////////////////////////////////////////// +- (void)dealloc { + if(self.key) { + CFRelease(self.key); + } +} +//////////////////////////////////////////////////////////////////////////// + +@end + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +@interface LCRTMSecurity () + +@property(nonatomic)BOOL isReady; //is the key processing done? +@property(nonatomic, strong)NSMutableArray *certificates; +@property(nonatomic, strong)NSMutableArray *pubKeys; +@property(nonatomic)BOOL usePublicKeys; + +@end + +@implementation LCRTMSecurity + +///////////////////////////////////////////////////////////////////////////// +- (instancetype)initUsingPublicKeys:(BOOL)publicKeys { + NSArray *paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"cer" inDirectory:@"."]; + NSMutableArray *collect = [NSMutableArray array]; + for(NSString *path in paths) { + NSData *data = [NSData dataWithContentsOfFile:path]; + if(data) { + [collect addObject:[[LCRTMSSLCert alloc] initWithData:data]]; + } + } + return [self initWithCerts:collect publicKeys:publicKeys]; +} +///////////////////////////////////////////////////////////////////////////// +- (instancetype)initWithCerts:(NSArray*)certs publicKeys:(BOOL)publicKeys { + if(self = [super init]) { + self.validatedDN = YES; + self.usePublicKeys = publicKeys; + if(self.usePublicKeys) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ + NSMutableArray *collect = [NSMutableArray array]; + for(LCRTMSSLCert *cert in certs) { + if(cert.certData && !cert.key) { + cert.key = [self extractPublicKey:cert.certData]; + } + if(cert.key) { + [collect addObject:CFBridgingRelease(cert.key)]; + } + } + self.certificates = collect; + self.isReady = YES; + }); + } else { + NSMutableArray *collect = [NSMutableArray array]; + for(LCRTMSSLCert *cert in certs) { + if(cert.certData) { + [collect addObject:cert.certData]; + } + } + self.certificates = collect; + self.isReady = YES; + } + } + return self; +} +///////////////////////////////////////////////////////////////////////////// +- (BOOL)isValid:(SecTrustRef)trust domain:(NSString*)domain { + int tries = 0; + while (!self.isReady) { + usleep(1000); + tries++; + if(tries > 5) { + return NO; //doesn't appear it is going to ever be ready... + } + } + BOOL status = NO; + SecPolicyRef policy; + if(self.validatedDN) { + policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)domain); + } else { + policy = SecPolicyCreateBasicX509(); + } + SecTrustSetPolicies(trust,policy); + if(self.usePublicKeys) { + for(id serverKey in [self publicKeyChainForTrust:trust]) { + for(id keyObj in self.pubKeys) { + if([serverKey isEqual:keyObj]) { + status = YES; + break; + } + } + } + } else { + NSArray *serverCerts = [self certificateChainForTrust:trust]; + NSMutableArray *collect = [NSMutableArray arrayWithCapacity:self.certificates.count]; + for(NSData *data in self.certificates) { + [collect addObject:CFBridgingRelease(SecCertificateCreateWithData(nil,(__bridge CFDataRef)data))]; + } + SecTrustSetAnchorCertificates(trust,(__bridge CFArrayRef)collect); + SecTrustResultType result = 0; + SecTrustEvaluate(trust,&result); + if(result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) { + NSInteger trustedCount = 0; + for(NSData *serverData in serverCerts) { + for(NSData *certData in self.certificates) { + if([certData isEqualToData:serverData]) { + trustedCount++; + break; + } + } + } + if(trustedCount == serverCerts.count) { + status = YES; + } + } + } + + CFRelease(policy); + return status; +} +///////////////////////////////////////////////////////////////////////////// +- (SecKeyRef)extractPublicKey:(NSData*)data { + SecCertificateRef possibleKey = SecCertificateCreateWithData(nil,(__bridge CFDataRef)data); + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecKeyRef key = [self extractPublicKeyFromCert:possibleKey policy:policy]; + CFRelease(policy); + CFRelease(possibleKey); + return key; +} +///////////////////////////////////////////////////////////////////////////// +- (SecKeyRef)extractPublicKeyFromCert:(SecCertificateRef)cert policy:(SecPolicyRef)policy { + + SecTrustRef trust; + SecTrustCreateWithCertificates(cert,policy,&trust); + SecTrustResultType result = kSecTrustResultInvalid; + SecTrustEvaluate(trust,&result); + SecKeyRef key = SecTrustCopyPublicKey(trust); + CFRelease(trust); + return key; +} +///////////////////////////////////////////////////////////////////////////// +- (NSArray*)certificateChainForTrust:(SecTrustRef)trust { + NSMutableArray *collect = [NSMutableArray array]; + for(int i = 0; i < SecTrustGetCertificateCount(trust); i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust,i); + if(cert) { + [collect addObject:CFBridgingRelease(SecCertificateCopyData(cert))]; + } + } + return collect; +} +///////////////////////////////////////////////////////////////////////////// +- (NSArray*)publicKeyChainForTrust:(SecTrustRef)trust { + NSMutableArray *collect = [NSMutableArray array]; + SecPolicyRef policy = SecPolicyCreateBasicX509(); + for(int i = 0; i < SecTrustGetCertificateCount(trust); i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust,i); + SecKeyRef key = [self extractPublicKeyFromCert:cert policy:policy]; + if(key) { + [collect addObject:CFBridgingRelease(key)]; + } + } + CFRelease(policy); + return collect; +} +///////////////////////////////////////////////////////////////////////////// + +@end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h new file mode 100644 index 000000000..604bdc9bc --- /dev/null +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h @@ -0,0 +1,159 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LCRTMWebSocket.h +// +// Created by Austin and Dalton Cherry on on 5/13/14. +// Copyright (c) 2014-2017 Austin Cherry. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#import +#import "LCRTMSecurity.h" + +@class LCRTMWebSocket; + +/** + It is important to note that all the delegate methods are put back on the main thread. + This means if you want to do some major process of the data, you need to create a background thread. + */ +@protocol LCRTMWebSocketDelegate + +@optional +/** + The websocket connected to its host. + @param socket is the current socket object. + */ +-(void)websocketDidConnect:(nonnull LCRTMWebSocket*)socket; + +/** + The websocket was disconnected from its host. + @param socket is the current socket object. + @param error is return an error occured to trigger the disconnect. + */ +-(void)websocketDidDisconnect:(nonnull LCRTMWebSocket*)socket error:(nullable NSError*)error; + +/** + The websocket got a text based message. + @param socket is the current socket object. + @param string is the text based data that has been returned. + */ +-(void)websocket:(nonnull LCRTMWebSocket*)socket didReceiveMessage:(nonnull NSString*)string; + +/** + The websocket got a binary based message. + @param socket is the current socket object. + @param data is the binary based data that has been returned. + */ +-(void)websocket:(nonnull LCRTMWebSocket*)socket didReceiveData:(nullable NSData*)data; + +@end + +@interface LCRTMWebSocket : NSObject + +@property(nonatomic,weak, nullable)iddelegate; +@property(nonatomic, readonly, nonnull) NSURL *url; + +/** + constructor to create a new websocket. + @param url the host you want to connect to. + @param protocols the websocket protocols you want to use (e.g. chat,superchat). + @return a newly initalized websocket. + */ +- (nonnull instancetype)initWithURL:(nonnull NSURL *)url protocols:(nullable NSArray*)protocols; + +/** + connect to the host. + */ +- (void)connect; + +/** + disconnect to the host. This sends the close Connection opcode to terminate cleanly. + */ +- (void)disconnect; + +/** + write binary based data to the socket. + @param data the binary data to write. + */ +- (void)writeData:(nonnull NSData*)data; + +/** + write text based data to the socket. + @param string the string to write. + */ +- (void)writeString:(nonnull NSString*)string; + +/** + write ping to the socket. + @param data the binary data to write (if desired). + */ +- (void)writePing:(nonnull NSData*)data; + +/** + Add a header to send along on the the HTTP connect. + @param value the string to send + @param key the HTTP key name to send + */ +- (void)addHeader:(nonnull NSString*)value forKey:(nonnull NSString*)key; + +/** + returns if the socket is conneted or not. + */ +@property(nonatomic, assign, readonly)BOOL isConnected; + +/** + Enable VOIP support on the socket, so it can be used in the background for VOIP calls. + Default setting is No. + */ +@property(nonatomic, assign)BOOL voipEnabled; + +/** + Allows connection to self signed or untrusted WebSocket connection. Useful for development. + Default setting is No. + */ +@property(nonatomic, assign)BOOL selfSignedSSL; + +/** + Use for SSL pinning. + */ +@property(nonatomic, strong, nullable)LCRTMSecurity *security; + +/** + Set your own custom queue. + Default setting is dispatch_get_main_queue. + */ +@property(nonatomic, strong, nullable)dispatch_queue_t queue; + +/** + Block property to use on connect. + */ +@property(nonatomic, strong, nullable)void (^onConnect)(void); + +/** + Block property to use on disconnect. + */ +@property(nonatomic, strong, nullable)void (^onDisconnect)(NSError*_Nullable); + +/** + Block property to use on receiving data. + */ +@property(nonatomic, strong, nullable)void (^onData)(NSData*_Nullable); + +/** + Block property to use on receiving text. + */ +@property(nonatomic, strong, nullable)void (^onText)(NSString*_Nullable); + +@end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m new file mode 100644 index 000000000..9230dfde3 --- /dev/null +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -0,0 +1,805 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LCRTMWebSocket.m +// +// Created by Austin and Dalton Cherry on on 5/13/14. +// Copyright (c) 2014-2017 Austin Cherry. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#import "LCRTMWebSocket.h" + +//get the opCode from the packet +typedef NS_ENUM(NSUInteger, LCRTMOpCode) { + LCRTMOpCodeContinueFrame = 0x0, + LCRTMOpCodeTextFrame = 0x1, + LCRTMOpCodeBinaryFrame = 0x2, + //3-7 are reserved. + LCRTMOpCodeConnectionClose = 0x8, + LCRTMOpCodePing = 0x9, + LCRTMOpCodePong = 0xA, + //B-F reserved. +}; + +typedef NS_ENUM(NSUInteger, LCRTMCloseCode) { + LCRTMCloseCodeNormal = 1000, + LCRTMCloseCodeGoingAway = 1001, + LCRTMCloseCodeProtocolError = 1002, + LCRTMCloseCodeProtocolUnhandledType = 1003, + // 1004 reserved. + LCRTMCloseCodeNoStatusReceived = 1005, + //1006 reserved. + LCRTMCloseCodeEncoding = 1007, + LCRTMCloseCodePolicyViolated = 1008, + LCRTMCloseCodeMessageTooBig = 1009 +}; + +typedef NS_ENUM(NSUInteger, LCRTMInternalErrorCode) { + // 0-999 WebSocket status codes not used + LCRTMOutputStreamWriteError = 1 +}; + +#define kLCRTMInternalHTTPStatusWebSocket 101 + +//holds the responses in our read stack to properly process messages +@interface LCRTMResponse : NSObject + +@property(nonatomic, assign)BOOL isFin; +@property(nonatomic, assign)LCRTMOpCode code; +@property(nonatomic, assign)NSInteger bytesLeft; +@property(nonatomic, assign)NSInteger frameCount; +@property(nonatomic, strong)NSMutableData *buffer; + +@end + +@interface LCRTMWebSocket () + +@property(nonatomic, strong, nonnull)NSURL *url; +@property(nonatomic, strong, null_unspecified)NSInputStream *inputStream; +@property(nonatomic, strong, null_unspecified)NSOutputStream *outputStream; +@property(nonatomic, strong, null_unspecified)NSOperationQueue *writeQueue; +@property(nonatomic, assign)BOOL isRunLoop; +@property(nonatomic, strong, nonnull)NSMutableArray *readStack; +@property(nonatomic, strong, nonnull)NSMutableArray *inputQueue; +@property(nonatomic, strong, nullable)NSData *fragBuffer; +@property(nonatomic, strong, nullable)NSMutableDictionary *headers; +@property(nonatomic, strong, nullable)NSArray *optProtocols; +@property(nonatomic, assign)BOOL isCreated; +@property(nonatomic, assign)BOOL didDisconnect; +@property(nonatomic, assign)BOOL certValidated; + +@end + +//Constant Header Values. +NS_ASSUME_NONNULL_BEGIN +static NSString *const headerWSUpgradeName = @"Upgrade"; +static NSString *const headerWSUpgradeValue = @"websocket"; +static NSString *const headerWSHostName = @"Host"; +static NSString *const headerWSConnectionName = @"Connection"; +static NSString *const headerWSConnectionValue = @"Upgrade"; +static NSString *const headerWSProtocolName = @"Sec-WebSocket-Protocol"; +static NSString *const headerWSVersionName = @"Sec-Websocket-Version"; +static NSString *const headerWSVersionValue = @"13"; +static NSString *const headerWSKeyName = @"Sec-WebSocket-Key"; +static NSString *const headerOriginName = @"Origin"; +static NSString *const headerWSAcceptName = @"Sec-WebSocket-Accept"; +NS_ASSUME_NONNULL_END + +//Class Constants +static char CRLFBytes[] = {'\r', '\n', '\r', '\n'}; +static int BUFFER_MAX = 4096; + +// This get the correct bits out by masking the bytes of the buffer. +static const uint8_t LCRTMFinMask = 0x80; +static const uint8_t LCRTMOpCodeMask = 0x0F; +static const uint8_t LCRTMRSVMask = 0x70; +static const uint8_t LCRTMMaskMask = 0x80; +static const uint8_t LCRTMPayloadLenMask = 0x7F; +static const size_t LCRTMMaxFrameSize = 32; + +@implementation LCRTMWebSocket + +///////////////////////////////////////////////////////////////////////////// +//Default initializer +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray*)protocols +{ + if(self = [super init]) { + self.certValidated = NO; + self.voipEnabled = NO; + self.selfSignedSSL = NO; + self.queue = dispatch_get_main_queue(); + self.url = url; + self.readStack = [NSMutableArray new]; + self.inputQueue = [NSMutableArray new]; + self.optProtocols = protocols; + } + + return self; +} +///////////////////////////////////////////////////////////////////////////// +//Exposed method for connecting to URL provided in init method. +- (void)connect { + if(self.isCreated) { + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue, ^{ + weakSelf.didDisconnect = NO; + }); + + //everything is on a background thread. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + weakSelf.isCreated = YES; + [weakSelf createHTTPRequest]; + weakSelf.isCreated = NO; + }); +} +///////////////////////////////////////////////////////////////////////////// +- (void)disconnect { + [self writeError:LCRTMCloseCodeNormal]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)writeString:(NSString*)string { + if(string) { + [self dequeueWrite:[string dataUsingEncoding:NSUTF8StringEncoding] + withCode:LCRTMOpCodeTextFrame]; + } +} +///////////////////////////////////////////////////////////////////////////// +- (void)writePing:(NSData*)data { + [self dequeueWrite:data withCode:LCRTMOpCodePing]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)writeData:(NSData*)data { + [self dequeueWrite:data withCode:LCRTMOpCodeBinaryFrame]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)addHeader:(NSString*)value forKey:(NSString*)key { + if(!self.headers) { + self.headers = [[NSMutableDictionary alloc] init]; + } + [self.headers setObject:value forKey:key]; +} +///////////////////////////////////////////////////////////////////////////// + +#pragma mark - connect's internal supporting methods + +///////////////////////////////////////////////////////////////////////////// + +- (NSString *)origin; +{ + NSString *scheme = [_url.scheme lowercaseString]; + + if ([scheme isEqualToString:@"wss"]) { + scheme = @"https"; + } else if ([scheme isEqualToString:@"ws"]) { + scheme = @"http"; + } + + if (_url.port) { + return [NSString stringWithFormat:@"%@://%@:%@/", scheme, _url.host, _url.port]; + } else { + return [NSString stringWithFormat:@"%@://%@/", scheme, _url.host]; + } +} + + +//Uses CoreFoundation to build a HTTP request to send over TCP stream. +- (void)createHTTPRequest { + CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)self.url.absoluteString, NULL); + CFStringRef requestMethod = CFSTR("GET"); + CFHTTPMessageRef urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, + requestMethod, + url, + kCFHTTPVersion1_1); + CFRelease(url); + + NSNumber *port = _url.port; + if (!port) { + if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]){ + port = @(443); + } else { + port = @(80); + } + } + NSString *protocols = nil; + if([self.optProtocols count] > 0) { + protocols = [self.optProtocols componentsJoinedByString:@","]; + } + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSHostName, + (__bridge CFStringRef)[NSString stringWithFormat:@"%@:%@",self.url.host,port]); + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSVersionName, + (__bridge CFStringRef)headerWSVersionValue); + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSKeyName, + (__bridge CFStringRef)[self generateWebSocketKey]); + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSUpgradeName, + (__bridge CFStringRef)headerWSUpgradeValue); + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSConnectionName, + (__bridge CFStringRef)headerWSConnectionValue); + if (protocols.length > 0) { + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerWSProtocolName, + (__bridge CFStringRef)protocols); + } + + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)headerOriginName, + (__bridge CFStringRef)[self origin]); + + for(NSString *key in self.headers) { + CFHTTPMessageSetHeaderFieldValue(urlRequest, + (__bridge CFStringRef)key, + (__bridge CFStringRef)self.headers[key]); + } + +#if defined(DEBUG) + NSLog(@"urlRequest = \"%@\"", urlRequest); +#endif + NSData *serializedRequest = (__bridge_transfer NSData *)(CFHTTPMessageCopySerializedMessage(urlRequest)); + [self initStreamsWithData:serializedRequest port:port]; + CFRelease(urlRequest); +} +///////////////////////////////////////////////////////////////////////////// +//Random String of 16 lowercase chars, SHA1 and base64 encoded. +- (NSString*)generateWebSocketKey { + NSInteger seed = 16; + NSMutableString *string = [NSMutableString stringWithCapacity:seed]; + for (int i = 0; i < seed; i++) { + [string appendFormat:@"%C", (unichar)('a' + arc4random_uniform(25))]; + } + return [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; +} +///////////////////////////////////////////////////////////////////////////// +//Sets up our reader/writer for the TCP stream. +- (void)initStreamsWithData:(NSData*)data port:(NSNumber*)port { + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.url.host, [port intValue], &readStream, &writeStream); + + self.inputStream = (__bridge_transfer NSInputStream *)readStream; + self.inputStream.delegate = self; + self.outputStream = (__bridge_transfer NSOutputStream *)writeStream; + self.outputStream.delegate = self; + if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]) { + [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; + [self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; + } else { + self.certValidated = YES; //not a https session, so no need to check SSL pinning + } + if(self.voipEnabled) { + [self.inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType]; + [self.outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType]; + } + if(self.selfSignedSSL) { + NSString *chain = (__bridge_transfer NSString *)kCFStreamSSLValidatesCertificateChain; + NSString *peerName = (__bridge_transfer NSString *)kCFStreamSSLValidatesCertificateChain; + NSString *key = (__bridge_transfer NSString *)kCFStreamPropertySSLSettings; + NSDictionary *settings = @{chain: [[NSNumber alloc] initWithBool:NO], + peerName: [NSNull null]}; + [self.inputStream setProperty:settings forKey:key]; + [self.outputStream setProperty:settings forKey:key]; + } + self.isRunLoop = YES; + [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.inputStream open]; + [self.outputStream open]; + size_t dataLen = [data length]; + [self.outputStream write:[data bytes] maxLength:dataLen]; + while (self.isRunLoop) { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } +} +///////////////////////////////////////////////////////////////////////////// + +#pragma mark - NSStreamDelegate + +///////////////////////////////////////////////////////////////////////////// +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { + if(self.security && !self.certValidated && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { + SecTrustRef trust = (__bridge SecTrustRef)([aStream propertyForKey:(__bridge_transfer NSString *)kCFStreamPropertySSLPeerTrust]); + NSString *domain = [aStream propertyForKey:(__bridge_transfer NSString *)kCFStreamSSLPeerName]; + if([self.security isValid:trust domain:domain]) { + self.certValidated = YES; + } else { + [self disconnectStream:[self errorWithDetail:@"Invalid SSL certificate" code:1]]; + return; + } + } + switch (eventCode) { + case NSStreamEventNone: + break; + + case NSStreamEventOpenCompleted: + break; + + case NSStreamEventHasBytesAvailable: + if(aStream == self.inputStream) { + [self processInputStream]; + } + break; + + case NSStreamEventHasSpaceAvailable: + break; + + case NSStreamEventErrorOccurred: + [self disconnectStream:[aStream streamError]]; + break; + + case NSStreamEventEndEncountered: + [self disconnectStream:nil]; + break; + + default: + break; + } +} +///////////////////////////////////////////////////////////////////////////// +- (void)disconnectStream:(NSError*)error { + [self.writeQueue waitUntilAllOperationsAreFinished]; + [self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.outputStream close]; + [self.inputStream close]; + self.outputStream = nil; + self.inputStream = nil; + self.isRunLoop = NO; + _isConnected = NO; + self.certValidated = NO; + [self doDisconnect:error]; +} +///////////////////////////////////////////////////////////////////////////// + +#pragma mark - Stream Processing Methods + +///////////////////////////////////////////////////////////////////////////// +- (void)processInputStream { + @autoreleasepool { + uint8_t buffer[BUFFER_MAX]; + NSInteger length = [self.inputStream read:buffer maxLength:BUFFER_MAX]; + if(length > 0) { + if(!self.isConnected) { + CFIndex responseStatusCode; + BOOL status = [self processHTTP:buffer length:length responseStatusCode:&responseStatusCode]; +#if defined(DEBUG) + if (length < BUFFER_MAX) { + buffer[length] = 0x00; + } else { + buffer[BUFFER_MAX - 1] = 0x00; + } + NSLog(@"response (%ld) = \"%s\"", responseStatusCode, buffer); +#endif + if(status == NO) { + [self doDisconnect:[self errorWithDetail:@"Invalid HTTP upgrade" code:1 userInfo:@{@"HTTPResponseStatusCode" : @(responseStatusCode)}]]; + } + } else { + BOOL process = NO; + if(self.inputQueue.count == 0) { + process = YES; + } + [self.inputQueue addObject:[NSData dataWithBytes:buffer length:length]]; + if(process) { + [self dequeueInput]; + } + } + } + } +} +///////////////////////////////////////////////////////////////////////////// +- (void)dequeueInput { + if(self.inputQueue.count > 0) { + NSData *data = [self.inputQueue objectAtIndex:0]; + NSData *work = data; + if(self.fragBuffer) { + NSMutableData *combine = [NSMutableData dataWithData:self.fragBuffer]; + [combine appendData:data]; + work = combine; + self.fragBuffer = nil; + } + [self processRawMessage:(uint8_t*)work.bytes length:work.length]; + [self.inputQueue removeObject:data]; + [self dequeueInput]; + } +} +///////////////////////////////////////////////////////////////////////////// +//Finds the HTTP Packet in the TCP stream, by looking for the CRLF. +- (BOOL)processHTTP:(uint8_t*)buffer length:(NSInteger)bufferLen responseStatusCode:(CFIndex*)responseStatusCode { + int k = 0; + NSInteger totalSize = 0; + for(int i = 0; i < bufferLen; i++) { + if(buffer[i] == CRLFBytes[k]) { + k++; + if(k == 3) { + totalSize = i + 1; + break; + } + } else { + k = 0; + } + } + if(totalSize > 0) { + BOOL status = [self validateResponse:buffer length:totalSize responseStatusCode:responseStatusCode]; + if (status == YES) { + _isConnected = YES; + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue,^{ + if([self.delegate respondsToSelector:@selector(websocketDidConnect:)]) { + [weakSelf.delegate websocketDidConnect:self]; + } + if(weakSelf.onConnect) { + weakSelf.onConnect(); + } + }); + totalSize += 1; //skip the last \n + NSInteger restSize = bufferLen-totalSize; + if(restSize > 0) { + [self processRawMessage:(buffer+totalSize) length:restSize]; + } + } + return status; + } + return NO; +} +///////////////////////////////////////////////////////////////////////////// +//Validate the HTTP is a 101, as per the RFC spec. +- (BOOL)validateResponse:(uint8_t *)buffer length:(NSInteger)bufferLen responseStatusCode:(CFIndex*)responseStatusCode { + CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, NO); + CFHTTPMessageAppendBytes(response, buffer, bufferLen); + *responseStatusCode = CFHTTPMessageGetResponseStatusCode(response); + BOOL status = ((*responseStatusCode) == kLCRTMInternalHTTPStatusWebSocket)?(YES):(NO); + if(status == NO) { + CFRelease(response); + return NO; + } + NSDictionary *headers = (__bridge_transfer NSDictionary *)(CFHTTPMessageCopyAllHeaderFields(response)); + NSString *acceptKey = headers[headerWSAcceptName]; + CFRelease(response); + if(acceptKey.length > 0) { + return YES; + } + return NO; +} +///////////////////////////////////////////////////////////////////////////// +-(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { + LCRTMResponse *response = [self.readStack lastObject]; + if(response && bufferLen < 2) { + self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; + return; + } + if(response.bytesLeft > 0) { + NSInteger len = response.bytesLeft; + NSInteger extra = bufferLen - response.bytesLeft; + if(response.bytesLeft > bufferLen) { + len = bufferLen; + extra = 0; + } + response.bytesLeft -= len; + [response.buffer appendData:[NSData dataWithBytes:buffer length:len]]; + [self processResponse:response]; + NSInteger offset = bufferLen - extra; + if(extra > 0) { + [self processExtra:(buffer+offset) length:extra]; + } + return; + } else { + if(bufferLen < 2) { // we need at least 2 bytes for the header + self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; + return; + } + BOOL isFin = (LCRTMFinMask & buffer[0]); + uint8_t receivedOpcode = (LCRTMOpCodeMask & buffer[0]); + BOOL isMasked = (LCRTMMaskMask & buffer[1]); + uint8_t payloadLen = (LCRTMPayloadLenMask & buffer[1]); + NSInteger offset = 2; //how many bytes do we need to skip for the header + if((isMasked || (LCRTMRSVMask & buffer[0])) && receivedOpcode != LCRTMOpCodePong) { + [self doDisconnect:[self errorWithDetail:@"masked and rsv data is not currently supported" code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + BOOL isControlFrame = (receivedOpcode == LCRTMOpCodeConnectionClose || receivedOpcode == LCRTMOpCodePing); + if(!isControlFrame && (receivedOpcode != LCRTMOpCodeBinaryFrame && receivedOpcode != LCRTMOpCodeContinueFrame && receivedOpcode != LCRTMOpCodeTextFrame && receivedOpcode != LCRTMOpCodePong)) { + [self doDisconnect:[self errorWithDetail:[NSString stringWithFormat:@"unknown opcode: 0x%x",receivedOpcode] code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + if(isControlFrame && !isFin) { + [self doDisconnect:[self errorWithDetail:@"control frames can't be fragmented" code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + if(receivedOpcode == LCRTMOpCodeConnectionClose) { + //the server disconnected us + uint16_t code = LCRTMCloseCodeNormal; + if(payloadLen == 1) { + code = LCRTMCloseCodeProtocolError; + } + else if(payloadLen > 1) { + code = CFSwapInt16BigToHost(*(uint16_t *)(buffer+offset) ); + if(code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000)) { + code = LCRTMCloseCodeProtocolError; + } + offset += 2; + } + + if(payloadLen > 2) { + NSInteger len = payloadLen-2; + if(len > 0) { + NSData *data = [NSData dataWithBytes:(buffer+offset) length:len]; + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if(!str) { + code = LCRTMCloseCodeProtocolError; + } + } + } + [self writeError:code]; + [self doDisconnect:[self errorWithDetail:@"continue frame before a binary or text frame" code:code]]; + return; + } + if(isControlFrame && payloadLen > 125) { + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + NSInteger dataLength = payloadLen; + if(payloadLen == 127) { + dataLength = (NSInteger)CFSwapInt64BigToHost(*(uint64_t *)(buffer+offset)); + offset += sizeof(uint64_t); + } else if(payloadLen == 126) { + dataLength = CFSwapInt16BigToHost(*(uint16_t *)(buffer+offset) ); + offset += sizeof(uint16_t); + } + if(bufferLen < offset) { // we cannot process this yet, nead more header data + self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; + return; + } + NSInteger len = dataLength; + if(dataLength > (bufferLen-offset) || (bufferLen - offset) < dataLength) { + len = bufferLen-offset; + } + NSData *data = nil; + if(len < 0) { + len = 0; + data = [NSData data]; + } else { + data = [NSData dataWithBytes:(buffer+offset) length:len]; + } + if(receivedOpcode == LCRTMOpCodePong) { + NSInteger step = (offset+len); + NSInteger extra = bufferLen-step; + if(extra > 0) { + [self processRawMessage:(buffer+step) length:extra]; + } + return; + } + LCRTMResponse *response = [self.readStack lastObject]; + if(isControlFrame) { + response = nil; //don't append pings + } + if(!isFin && receivedOpcode == LCRTMOpCodeContinueFrame && !response) { + [self doDisconnect:[self errorWithDetail:@"continue frame before a binary or text frame" code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + BOOL isNew = NO; + if(!response) { + if(receivedOpcode == LCRTMOpCodeContinueFrame) { + [self doDisconnect:[self errorWithDetail:@"first frame can't be a continue frame" code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + isNew = YES; + response = [LCRTMResponse new]; + response.code = receivedOpcode; + response.bytesLeft = dataLength; + response.buffer = [NSMutableData dataWithData:data]; + } else { + if(receivedOpcode == LCRTMOpCodeContinueFrame) { + response.bytesLeft = dataLength; + } else { + [self doDisconnect:[self errorWithDetail:@"second and beyond of fragment message must be a continue frame" code:LCRTMCloseCodeProtocolError]]; + [self writeError:LCRTMCloseCodeProtocolError]; + return; + } + [response.buffer appendData:data]; + } + response.bytesLeft -= len; + response.frameCount++; + response.isFin = isFin; + if(isNew) { + [self.readStack addObject:response]; + } + [self processResponse:response]; + + NSInteger step = (offset+len); + NSInteger extra = bufferLen-step; + if(extra > 0) { + [self processExtra:(buffer+step) length:extra]; + } + } + +} +///////////////////////////////////////////////////////////////////////////// +- (void)processExtra:(uint8_t*)buffer length:(NSInteger)bufferLen { + if(bufferLen < 2) { + self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; + } else { + [self processRawMessage:buffer length:bufferLen]; + } +} +///////////////////////////////////////////////////////////////////////////// +- (BOOL)processResponse:(LCRTMResponse*)response { + if(response.isFin && response.bytesLeft <= 0) { + NSData *data = response.buffer; + if(response.code == LCRTMOpCodePing) { + [self dequeueWrite:response.buffer withCode:LCRTMOpCodePong]; + } else if(response.code == LCRTMOpCodeTextFrame) { + NSString *str = [[NSString alloc] initWithData:response.buffer encoding:NSUTF8StringEncoding]; + if(!str) { + [self writeError:LCRTMCloseCodeEncoding]; + return NO; + } + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue,^{ + if([weakSelf.delegate respondsToSelector:@selector(websocket:didReceiveMessage:)]) { + [weakSelf.delegate websocket:weakSelf didReceiveMessage:str]; + } + if(weakSelf.onText) { + weakSelf.onText(str); + } + }); + } else if(response.code == LCRTMOpCodeBinaryFrame) { + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue,^{ + if([weakSelf.delegate respondsToSelector:@selector(websocket:didReceiveData:)]) { + [weakSelf.delegate websocket:weakSelf didReceiveData:data]; + } + if(weakSelf.onData) { + weakSelf.onData(data); + } + }); + } + [self.readStack removeLastObject]; + return YES; + } + return NO; +} +///////////////////////////////////////////////////////////////////////////// +-(void)dequeueWrite:(NSData*)data withCode:(LCRTMOpCode)code { + if(!self.isConnected) { + return; + } + if(!self.writeQueue) { + self.writeQueue = [[NSOperationQueue alloc] init]; + self.writeQueue.maxConcurrentOperationCount = 1; + } + + __weak typeof(self) weakSelf = self; + [self.writeQueue addOperationWithBlock:^{ + if(!weakSelf || !weakSelf.isConnected) { + return; + } + typeof(weakSelf) strongSelf = weakSelf; + uint64_t offset = 2; //how many bytes do we need to skip for the header + uint8_t *bytes = (uint8_t*)[data bytes]; + uint64_t dataLength = data.length; + NSMutableData *frame = [[NSMutableData alloc] initWithLength:(NSInteger)(dataLength + LCRTMMaxFrameSize)]; + uint8_t *buffer = (uint8_t*)[frame mutableBytes]; + buffer[0] = LCRTMFinMask | code; + if(dataLength < 126) { + buffer[1] |= dataLength; + } else if(dataLength <= UINT16_MAX) { + buffer[1] |= 126; + *((uint16_t *)(buffer + offset)) = CFSwapInt16BigToHost((uint16_t)dataLength); + offset += sizeof(uint16_t); + } else { + buffer[1] |= 127; + *((uint64_t *)(buffer + offset)) = CFSwapInt64BigToHost((uint64_t)dataLength); + offset += sizeof(uint64_t); + } + BOOL isMask = YES; + if(isMask) { + buffer[1] |= LCRTMMaskMask; + uint8_t *mask_key = (buffer + offset); + (void)SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); + offset += sizeof(uint32_t); + + for (size_t i = 0; i < dataLength; i++) { + buffer[offset] = bytes[i] ^ mask_key[i % sizeof(uint32_t)]; + offset += 1; + } + } else { + for(size_t i = 0; i < dataLength; i++) { + buffer[offset] = bytes[i]; + offset += 1; + } + } + uint64_t total = 0; + while (true) { + if(!strongSelf.isConnected || !strongSelf.outputStream) { + break; + } + NSInteger len = [strongSelf.outputStream write:([frame bytes]+total) maxLength:(NSInteger)(offset-total)]; + if(len < 0 || len == NSNotFound) { + NSError *error = strongSelf.outputStream.streamError; + if(!error) { + error = [strongSelf errorWithDetail:@"output stream error during write" code:LCRTMOutputStreamWriteError]; + } + [strongSelf doDisconnect:error]; + break; + } else { + total += len; + } + if(total >= offset) { + break; + } + } + }]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)doDisconnect:(NSError*)error { + if(!self.didDisconnect) { + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue, ^{ + weakSelf.didDisconnect = YES; + [weakSelf disconnect]; + if([weakSelf.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) { + [weakSelf.delegate websocketDidDisconnect:weakSelf error:error]; + } + if(weakSelf.onDisconnect) { + weakSelf.onDisconnect(error); + } + }); + } +} +///////////////////////////////////////////////////////////////////////////// +- (NSError*)errorWithDetail:(NSString*)detail code:(NSInteger)code +{ + return [self errorWithDetail:detail code:code userInfo:nil]; +} +- (NSError*)errorWithDetail:(NSString*)detail code:(NSInteger)code userInfo:(NSDictionary *)userInfo +{ + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + details[detail] = NSLocalizedDescriptionKey; + if (userInfo) { + [details addEntriesFromDictionary:userInfo]; + } + return [[NSError alloc] initWithDomain:@"LCRTMWebSocket" code:code userInfo:details]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)writeError:(uint16_t)code { + uint16_t buffer[1]; + buffer[0] = CFSwapInt16BigToHost(code); + [self dequeueWrite:[NSData dataWithBytes:buffer length:sizeof(uint16_t)] withCode:LCRTMOpCodeConnectionClose]; +} +///////////////////////////////////////////////////////////////////////////// +- (void)dealloc { + if(self.isConnected) { + [self disconnect]; + } +} +///////////////////////////////////////////////////////////////////////////// +@end + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +@implementation LCRTMResponse + +@end +///////////////////////////////////////////////////////////////////////////// From 253eac5966d1c2216e241a8289059676fed2d0af Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Tue, 24 Dec 2019 13:09:52 +0800 Subject: [PATCH 09/21] test: add us old app --- AVOS/TestsBase/LCTestBase.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/AVOS/TestsBase/LCTestBase.swift b/AVOS/TestsBase/LCTestBase.swift index c48d32823..88fe15a8a 100644 --- a/AVOS/TestsBase/LCTestBase.swift +++ b/AVOS/TestsBase/LCTestBase.swift @@ -36,7 +36,7 @@ class LCTestBase: XCTestCase { } else { let testApp: TestApp = .ChinaNorth switch testApp { - case .ChinaNorth, .ChinaEast: + case .ChinaNorth, .ChinaEast, .USOld: AVOSCloud.setApplicationId( testApp.appInfo.id, clientKey: testApp.appInfo.key, @@ -65,6 +65,7 @@ extension LCTestBase { case ChinaNorth case ChinaEast case US + case USOld var appInfo: (id: String, key: String, serverURL: String) { switch self { case .ChinaNorth: @@ -76,9 +77,13 @@ extension LCTestBase { key: "T3TEAIcL8Ls5XGPsGz41B1bz", serverURL: "https://skhivsqi.lc-cn-e1-shared.com") case .US: - return (id: "eX7urCufwLd6X5mHxt7V12nL-MdYXbMMI", - key: "PrmzHPnRXjXezS54KryuHMG6", + return (id: "jenSt9nvWtuJtmurdE28eg5M-MdYXbMMI", + key: "8VLPsDlskJi8KsKppED4xKS0", serverURL: "") + case .USOld: + return (id: "kknqydxqd9wdq4cboy1dvvug5ha0ce3i2mrerrdrmr6pla1p", + key: "fate582pwsfh97s9o99nw91a152i7ndm9tsy866e6wpezth4", + serverURL: "https://beta-us.leancloud.cn") } } } From 1164ae830e41564ddd9059a15fffb8e10cc757a0 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Tue, 24 Dec 2019 16:50:37 +0800 Subject: [PATCH 10/21] chore: websocket should not set origin header. --- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index 9230dfde3..d6eb2d6d5 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -240,9 +240,12 @@ - (void)createHTTPRequest { (__bridge CFStringRef)protocols); } + /// Objc SDK should not set Origin Header. + /* CFHTTPMessageSetHeaderFieldValue(urlRequest, (__bridge CFStringRef)headerOriginName, (__bridge CFStringRef)[self origin]); + */ for(NSString *key in self.headers) { CFHTTPMessageSetHeaderFieldValue(urlRequest, From a51122b05efb62a64df924b080beddf774fbfbf7 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Tue, 24 Dec 2019 20:28:08 +0800 Subject: [PATCH 11/21] refactor(rtm): update websocket --- .../WebSocket/AVIMWebSocketWrapper.m | 80 +++++++------------ AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h | 28 ++----- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 53 ++++++------ 3 files changed, 63 insertions(+), 98 deletions(-) diff --git a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m index c103fcea6..3b790a1e9 100644 --- a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m +++ b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m @@ -8,7 +8,7 @@ #import "AVOSCloudIM.h" #import "AVIMWebSocketWrapper.h" -#import "AVIMWebSocket.h" +#import "LCRTMWebSocket.h" #import "AVIMErrorUtil.h" #import "AVIMCommon_Internal.h" #import "AVIMClient_Internal.h" @@ -142,7 +142,7 @@ - (NSError *)decodingError:(AVIMGenericCommand *)command // MARK: - AVIMWebSocketWrapper -@interface AVIMWebSocketWrapper () +@interface AVIMWebSocketWrapper () @property (atomic, assign) BOOL isApplicationInBackground; @property (atomic, assign) LCNetworkReachabilityStatus currentNetworkReachabilityStatus; @@ -171,7 +171,7 @@ @implementation AVIMWebSocketWrapper { dispatch_source_t _timeoutCheckerTimerSource; - AVIMWebSocket *_websocket; + LCRTMWebSocket *_websocket; LCNetworkReachabilityManager *_reachabilityMonitor; BOOL _didInitNetworkReachabilityStatus; @@ -345,8 +345,8 @@ - (void)handleApplicationStateIsInBackground:(BOOL)isInBackground - (void)openWithCallback:(void (^)(BOOL succeeded, NSError *error))callback { [self addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { - AVIMWebSocket *websocket = websocketWrapper->_websocket; - if (websocket && websocket.readyState == AVIMWebSocketStateConnected) { + LCRTMWebSocket *websocket = websocketWrapper->_websocket; + if (websocket && websocket.isConnected) { callback(true, nil); } else { [websocketWrapper purgeWithError:({ @@ -383,11 +383,10 @@ - (void)close // MARK: - Websocket Open & Close Notification -- (void)webSocketDidOpen:(AVIMWebSocket *)webSocket -{ +- (void)websocketDidConnect:(LCRTMWebSocket *)socket { AssertRunInQueue(self->_internalSerialQueue); - NSParameterAssert(webSocket == self->_websocket); - AVLoggerInfo(AVLoggerDomainIM, @" websocket did open.", webSocket); + NSParameterAssert(socket == self->_websocket); + AVLoggerInfo(AVLoggerDomainIM, @" websocket did open.", socket); if (self->_openTimeoutBlock) { dispatch_block_cancel(self->_openTimeoutBlock); self->_openTimeoutBlock = nil; @@ -405,29 +404,10 @@ - (void)webSocketDidOpen:(AVIMWebSocket *)webSocket } } -- (void)webSocket:(AVIMWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean -{ - // handle close by remote. - AssertRunInQueue(self->_internalSerialQueue); - NSParameterAssert(webSocket == self->_websocket); - NSError *error = [NSError errorWithDomain:AVIMWebSocketErrorDomain code:code userInfo:({ - NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary]; - if (reason) { mutableDictionary[@"reason"] = reason; } - mutableDictionary[@"wasClean"] = @(wasClean); - mutableDictionary; - })]; - AVLoggerError(AVLoggerDomainIM, @" websocket did close by remote with error: %@.", webSocket, error); - self->_useSecondaryServer = !self->_useSecondaryServer; - [self purgeWithError:error]; - [self pauseWithError:error]; - [self tryConnecting:false]; -} - -- (void)webSocket:(AVIMWebSocket *)webSocket didFailWithError:(NSError *)error -{ +- (void)websocketDidDisconnect:(LCRTMWebSocket *)socket error:(NSError *)error { AssertRunInQueue(self->_internalSerialQueue); - NSParameterAssert(webSocket == self->_websocket); - AVLoggerError(AVLoggerDomainIM, @" websocket did encounter error: %@.", webSocket, error); + NSParameterAssert(socket == self->_websocket); + AVLoggerError(AVLoggerDomainIM, @" websocket did disconnect with error: %@.", socket, error); self->_useSecondaryServer = !self->_useSecondaryServer; [self purgeWithError:error]; [self pauseWithError:error]; @@ -442,9 +422,9 @@ - (void)sendCommandWrapper:(LCIMProtobufCommandWrapper *)commandWrapper if (!commandWrapper || !commandWrapper.outCommand) { return; } - AVIMWebSocket *webSocket = websocketWrapper->_websocket; + LCRTMWebSocket *webSocket = websocketWrapper->_websocket; id delegate = websocketWrapper->_delegate; - if (!webSocket || webSocket.readyState != AVIMWebSocketStateConnected) { + if (!webSocket || !webSocket.isConnected) { commandWrapper.error = ({ AVIMErrorCode code = AVIMErrorCodeConnectionLost; LCError(code, AVIMErrorMessage(code), nil); @@ -473,16 +453,15 @@ - (void)sendCommandWrapper:(LCIMProtobufCommandWrapper *)commandWrapper [websocketWrapper->_serialIndexArray addObject:index]; } AVLoggerInfo(AVLoggerDomainIM, LCIM_OUT_COMMAND_LOG_FORMAT, [commandWrapper.outCommand avim_description]); - [webSocket send:data]; + [webSocket writeData:data]; }]; } -- (void)webSocket:(AVIMWebSocket *)webSocket didReceiveMessage:(id)message -{ +- (void)websocket:(LCRTMWebSocket *)socket didReceiveData:(NSData *)data { AssertRunInQueue(self->_internalSerialQueue); AVIMGenericCommand *inCommand = ({ NSError *error = nil; - AVIMGenericCommand *inCommand = [AVIMGenericCommand parseFromData:message error:&error]; + AVIMGenericCommand *inCommand = [AVIMGenericCommand parseFromData:data error:&error]; if (!inCommand) { AVLoggerError(AVLoggerDomainIM, @"did receive message with error: %@", error); return; @@ -540,19 +519,18 @@ - (void)tryStopPingSender - (void)sendPing { AssertRunInQueue(self->_internalSerialQueue); - AVIMWebSocket *websocket = self->_websocket; - if (!websocket || websocket.readyState != AVIMWebSocketStateConnected) { + LCRTMWebSocket *websocket = self->_websocket; + if (!websocket || !websocket.isConnected) { return; } AVLoggerInfo(AVLoggerDomainIM, @" websocket send ping.", websocket); self->_lastPingTimestamp = NSDate.date.timeIntervalSince1970; - [websocket sendPing:[@"" dataUsingEncoding:NSUTF8StringEncoding]]; + [websocket writePing:[NSData data]]; } -- (void)webSocket:(AVIMWebSocket *)webSocket didReceivePong:(id)data -{ +- (void)websocket:(LCRTMWebSocket *)socket didReceivePong:(NSData *)data { AssertRunInQueue(self->_internalSerialQueue); - AVLoggerInfo(AVLoggerDomainIM, @" websocket did receive pong.", webSocket); + AVLoggerInfo(AVLoggerDomainIM, @" websocket did receive pong.", socket); self->_didReceivePong = true; } @@ -723,14 +701,10 @@ - (void)tryConnecting:(BOOL)force [self notifyInReconnecting]; NSArray *protocols = RTMProtocols(); NSURL *URL = [NSURL URLWithString:server]; - if (protocols.count > 0) { - self->_websocket = [[AVIMWebSocket alloc] initWithURL:URL protocols:protocols]; - } else { - self->_websocket = [[AVIMWebSocket alloc] initWithURL:URL]; - } - [self->_websocket setDelegateDispatchQueue:self->_internalSerialQueue]; - [self->_websocket setDelegate:self]; - [self->_websocket open]; + self->_websocket = [[LCRTMWebSocket alloc] initWithURL:URL protocols:protocols]; + self->_websocket.queue = self->_internalSerialQueue; + self->_websocket.delegate = self; + [self->_websocket connect]; __weak typeof(self) weakSelf = self; self->_openTimeoutBlock = dispatch_block_create(0, ^{ self->_openTimeoutBlock = nil; @@ -782,8 +756,8 @@ - (void)purgeWithError:(NSError *)error // discard websocket if (self->_websocket) { AVLoggerInfo(AVLoggerDomainIM, @" websocket discard.", self->_websocket); - [self->_websocket setDelegate:nil]; - [self->_websocket close]; + self->_websocket.delegate = nil; + [self->_websocket disconnect]; self->_websocket = nil; } if (self->_openTimeoutBlock) { diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h index 604bdc9bc..7c91d9e05 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h @@ -58,6 +58,12 @@ */ -(void)websocket:(nonnull LCRTMWebSocket*)socket didReceiveData:(nullable NSData*)data; +/** + The websocket got a pong. + @param socket is the current socket object. + */ +-(void)websocket:(nonnull LCRTMWebSocket*)socket didReceivePong:(nullable NSData*)data; + @end @interface LCRTMWebSocket : NSObject @@ -111,7 +117,7 @@ /** returns if the socket is conneted or not. */ -@property(nonatomic, assign, readonly)BOOL isConnected; +@property(atomic, assign, readonly)BOOL isConnected; /** Enable VOIP support on the socket, so it can be used in the background for VOIP calls. @@ -136,24 +142,4 @@ */ @property(nonatomic, strong, nullable)dispatch_queue_t queue; -/** - Block property to use on connect. - */ -@property(nonatomic, strong, nullable)void (^onConnect)(void); - -/** - Block property to use on disconnect. - */ -@property(nonatomic, strong, nullable)void (^onDisconnect)(NSError*_Nullable); - -/** - Block property to use on receiving data. - */ -@property(nonatomic, strong, nullable)void (^onData)(NSData*_Nullable); - -/** - Block property to use on receiving text. - */ -@property(nonatomic, strong, nullable)void (^onText)(NSString*_Nullable); - @end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index d6eb2d6d5..ad882533a 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -76,6 +76,7 @@ @interface LCRTMWebSocket () @property(nonatomic, strong, nullable)NSData *fragBuffer; @property(nonatomic, strong, nullable)NSMutableDictionary *headers; @property(nonatomic, strong, nullable)NSArray *optProtocols; +@property(atomic, assign)BOOL isConnected; @property(nonatomic, assign)BOOL isCreated; @property(nonatomic, assign)BOOL didDisconnect; @property(nonatomic, assign)BOOL certValidated; @@ -365,7 +366,7 @@ - (void)disconnectStream:(NSError*)error { self.outputStream = nil; self.inputStream = nil; self.isRunLoop = NO; - _isConnected = NO; + self.isConnected = NO; self.certValidated = NO; [self doDisconnect:error]; } @@ -441,14 +442,13 @@ - (BOOL)processHTTP:(uint8_t*)buffer length:(NSInteger)bufferLen responseStatusC if(totalSize > 0) { BOOL status = [self validateResponse:buffer length:totalSize responseStatusCode:responseStatusCode]; if (status == YES) { - _isConnected = YES; + self.isConnected = YES; __weak typeof(self) weakSelf = self; dispatch_async(self.queue,^{ - if([self.delegate respondsToSelector:@selector(websocketDidConnect:)]) { - [weakSelf.delegate websocketDidConnect:self]; - } - if(weakSelf.onConnect) { - weakSelf.onConnect(); + LCRTMWebSocket *strongSelf = weakSelf; + if (!strongSelf) { return; } + if([strongSelf respondsToSelector:@selector(websocketDidConnect:)]) { + [strongSelf.delegate websocketDidConnect:strongSelf]; } }); totalSize += 1; //skip the last \n @@ -584,6 +584,14 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { data = [NSData dataWithBytes:(buffer+offset) length:len]; } if(receivedOpcode == LCRTMOpCodePong) { + __weak typeof(self) weakSelf = self; + dispatch_async(self.queue,^{ + LCRTMWebSocket *strongSelf = weakSelf; + if (!strongSelf) { return; } + if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceivePong:)]) { + [strongSelf.delegate websocket:strongSelf didReceivePong:data]; + } + }); NSInteger step = (offset+len); NSInteger extra = bufferLen-step; if(extra > 0) { @@ -660,21 +668,19 @@ - (BOOL)processResponse:(LCRTMResponse*)response { } __weak typeof(self) weakSelf = self; dispatch_async(self.queue,^{ - if([weakSelf.delegate respondsToSelector:@selector(websocket:didReceiveMessage:)]) { - [weakSelf.delegate websocket:weakSelf didReceiveMessage:str]; - } - if(weakSelf.onText) { - weakSelf.onText(str); + LCRTMWebSocket *strongSelf = weakSelf; + if (!strongSelf) { return; } + if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceiveMessage:)]) { + [strongSelf.delegate websocket:strongSelf didReceiveMessage:str]; } }); } else if(response.code == LCRTMOpCodeBinaryFrame) { __weak typeof(self) weakSelf = self; dispatch_async(self.queue,^{ - if([weakSelf.delegate respondsToSelector:@selector(websocket:didReceiveData:)]) { - [weakSelf.delegate websocket:weakSelf didReceiveData:data]; - } - if(weakSelf.onData) { - weakSelf.onData(data); + LCRTMWebSocket *strongSelf = weakSelf; + if (!strongSelf) { return; } + if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceiveData:)]) { + [strongSelf.delegate websocket:strongSelf didReceiveData:data]; } }); } @@ -760,13 +766,12 @@ - (void)doDisconnect:(NSError*)error { if(!self.didDisconnect) { __weak typeof(self) weakSelf = self; dispatch_async(self.queue, ^{ - weakSelf.didDisconnect = YES; - [weakSelf disconnect]; - if([weakSelf.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) { - [weakSelf.delegate websocketDidDisconnect:weakSelf error:error]; - } - if(weakSelf.onDisconnect) { - weakSelf.onDisconnect(error); + LCRTMWebSocket *strongSelf = weakSelf; + if (!strongSelf) { return; } + strongSelf.didDisconnect = YES; + [strongSelf disconnect]; + if([strongSelf.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) { + [strongSelf.delegate websocketDidDisconnect:strongSelf error:error]; } }); } From 7e3990bbf3824ad0520131f8738a3b1724918998 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Mon, 3 Feb 2020 17:59:20 +0800 Subject: [PATCH 12/21] chore: backup --- .../WebSocket/AVIMWebSocketWrapper.m | 8 + AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h | 7 +- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 137 ++++++++++++------ 3 files changed, 102 insertions(+), 50 deletions(-) diff --git a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m index 3b790a1e9..0c4842966 100644 --- a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m +++ b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m @@ -407,6 +407,10 @@ - (void)websocketDidConnect:(LCRTMWebSocket *)socket { - (void)websocketDidDisconnect:(LCRTMWebSocket *)socket error:(NSError *)error { AssertRunInQueue(self->_internalSerialQueue); NSParameterAssert(socket == self->_websocket); + if (!error) { + NSString *reason = @"Connection did close by remote peer."; + error = LCError(AVIMErrorCodeConnectionLost, reason, nil); + } AVLoggerError(AVLoggerDomainIM, @" websocket did disconnect with error: %@.", socket, error); self->_useSecondaryServer = !self->_useSecondaryServer; [self purgeWithError:error]; @@ -489,6 +493,10 @@ - (void)websocket:(LCRTMWebSocket *)socket didReceiveData:(NSData *)data { } } +- (void)websocket:(LCRTMWebSocket *)socket didReceiveMessage:(NSString *)string { + /// NOP +} + // MARK: - Ping Sender - (void)startPingSender diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h index 7c91d9e05..067ca6add 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h @@ -30,7 +30,6 @@ */ @protocol LCRTMWebSocketDelegate -@optional /** The websocket connected to its host. @param socket is the current socket object. @@ -68,8 +67,8 @@ @interface LCRTMWebSocket : NSObject -@property(nonatomic,weak, nullable)iddelegate; -@property(nonatomic, readonly, nonnull) NSURL *url; +@property(nonatomic, weak, nullable)id delegate; +@property(nonatomic, strong, readonly, nonnull)NSURL *url; /** constructor to create a new websocket. @@ -140,6 +139,6 @@ Set your own custom queue. Default setting is dispatch_get_main_queue. */ -@property(nonatomic, strong, nullable)dispatch_queue_t queue; +@property(nonatomic, strong, nonnull)dispatch_queue_t queue; @end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index ad882533a..056291798 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -40,7 +40,7 @@ typedef NS_ENUM(NSUInteger, LCRTMCloseCode) { LCRTMCloseCodeProtocolUnhandledType = 1003, // 1004 reserved. LCRTMCloseCodeNoStatusReceived = 1005, - //1006 reserved. + // 1006 reserved. LCRTMCloseCodeEncoding = 1007, LCRTMCloseCodePolicyViolated = 1008, LCRTMCloseCodeMessageTooBig = 1009 @@ -48,7 +48,11 @@ typedef NS_ENUM(NSUInteger, LCRTMCloseCode) { typedef NS_ENUM(NSUInteger, LCRTMInternalErrorCode) { // 0-999 WebSocket status codes not used - LCRTMOutputStreamWriteError = 1 + LCRTMOutputStreamWriteError = 1, //Output stream error during write + LCRTMInvalidSSLError = 2, //Invalid SSL certificate + LCRTMWriteTimeoutError = 3, //The socket timed out waiting to be ready to write + LCRTMUpgradeError = 4, //There was an error during the HTTP upgrade + LCRTMCloseError = 5 //There was an error during the close (socket probably has been dereferenced) }; #define kLCRTMInternalHTTPStatusWebSocket 101 @@ -67,9 +71,10 @@ @interface LCRTMResponse : NSObject @interface LCRTMWebSocket () @property(nonatomic, strong, nonnull)NSURL *url; -@property(nonatomic, strong, null_unspecified)NSInputStream *inputStream; -@property(nonatomic, strong, null_unspecified)NSOutputStream *outputStream; -@property(nonatomic, strong, null_unspecified)NSOperationQueue *writeQueue; +@property(nonatomic, strong, nonnull)NSOperationQueue *writeQueue; +@property(nonatomic, strong, nonnull)dispatch_queue_t streamQueue; +@property(nonatomic, strong)NSInputStream *inputStream; +@property(nonatomic, strong)NSOutputStream *outputStream; @property(nonatomic, assign)BOOL isRunLoop; @property(nonatomic, strong, nonnull)NSMutableArray *readStack; @property(nonatomic, strong, nonnull)NSMutableArray *inputQueue; @@ -116,17 +121,18 @@ @implementation LCRTMWebSocket //Default initializer - (instancetype)initWithURL:(NSURL *)url protocols:(NSArray*)protocols { - if(self = [super init]) { + self = [super init]; + if (self) { self.certValidated = NO; self.voipEnabled = NO; self.selfSignedSSL = NO; self.queue = dispatch_get_main_queue(); + self.streamQueue = dispatch_queue_create("LCRTMWebSocket.streamQueue", DISPATCH_QUEUE_SERIAL); self.url = url; self.readStack = [NSMutableArray new]; self.inputQueue = [NSMutableArray new]; self.optProtocols = protocols; } - return self; } ///////////////////////////////////////////////////////////////////////////// @@ -182,7 +188,7 @@ - (void)addHeader:(NSString*)value forKey:(NSString*)key { - (NSString *)origin; { - NSString *scheme = [_url.scheme lowercaseString]; + NSString *scheme = [self.url.scheme lowercaseString]; if ([scheme isEqualToString:@"wss"]) { scheme = @"https"; @@ -190,10 +196,10 @@ - (NSString *)origin; scheme = @"http"; } - if (_url.port) { - return [NSString stringWithFormat:@"%@://%@:%@/", scheme, _url.host, _url.port]; + if (self.url.port) { + return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.url.host, self.url.port]; } else { - return [NSString stringWithFormat:@"%@://%@/", scheme, _url.host]; + return [NSString stringWithFormat:@"%@://%@/", scheme, self.url.host]; } } @@ -208,7 +214,7 @@ - (void)createHTTPRequest { kCFHTTPVersion1_1); CFRelease(url); - NSNumber *port = _url.port; + NSNumber *port = self.url.port; if (!port) { if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]){ port = @(443); @@ -280,7 +286,8 @@ - (void)initStreamsWithData:(NSData*)data port:(NSNumber*)port { self.inputStream = (__bridge_transfer NSInputStream *)readStream; self.inputStream.delegate = self; - self.outputStream = (__bridge_transfer NSOutputStream *)writeStream; + NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream; + self.outputStream = outputStream; self.outputStream.delegate = self; if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]) { [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; @@ -301,13 +308,37 @@ - (void)initStreamsWithData:(NSData*)data port:(NSNumber*)port { [self.inputStream setProperty:settings forKey:key]; [self.outputStream setProperty:settings forKey:key]; } + CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, self.streamQueue); + CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, self.streamQueue); + [self.inputStream open]; + [self.outputStream open]; + + __weak typeof(self) weakSelf = self; + dispatch_async(self.streamQueue, ^{ + NSTimeInterval timeout = (60 * 1000000); + while (!outputStream.hasSpaceAvailable) { + usleep(100); + timeout -= 100; + if (timeout < 0) { + NSError *error = [LCRTMWebSocket errorWithDetail:@"Timed out waiting for the socket to be ready for a write" code:LCRTMWriteTimeoutError]; + [weakSelf disconnectStream:error]; + return; + } else if (outputStream.streamError) { + [weakSelf disconnectStream:outputStream.streamError]; + return; + } else if (!weakSelf) { + return; + } + } + NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ + [outputStream write:[data bytes] maxLength:[data length]]; + }]; + [weakSelf.writeQueue addOperation:op]; + }); + self.isRunLoop = YES; [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [self.inputStream open]; - [self.outputStream open]; - size_t dataLen = [data length]; - [self.outputStream write:[data bytes] maxLength:dataLen]; while (self.isRunLoop) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } @@ -324,7 +355,7 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { if([self.security isValid:trust domain:domain]) { self.certValidated = YES; } else { - [self disconnectStream:[self errorWithDetail:@"Invalid SSL certificate" code:1]]; + [self disconnectStream:[LCRTMWebSocket errorWithDetail:@"Invalid SSL certificate" code:1]]; return; } } @@ -358,16 +389,28 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { } ///////////////////////////////////////////////////////////////////////////// - (void)disconnectStream:(NSError*)error { - [self.writeQueue waitUntilAllOperationsAreFinished]; - [self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [self.outputStream close]; - [self.inputStream close]; - self.outputStream = nil; - self.inputStream = nil; + if (error) { + [self.writeQueue cancelAllOperations]; + } else { + [self.writeQueue waitUntilAllOperationsAreFinished]; + } + if (self.inputStream) { + self.inputStream.delegate = nil; + CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, NULL); + [self.inputStream close]; + self.inputStream = nil; + } + if (self.outputStream) { + self.outputStream.delegate = nil; + CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, NULL); + [self.outputStream close]; + self.outputStream = nil; + } + self.isRunLoop = NO; self.isConnected = NO; self.certValidated = NO; + [self doDisconnect:error]; } ///////////////////////////////////////////////////////////////////////////// @@ -392,7 +435,7 @@ - (void)processInputStream { NSLog(@"response (%ld) = \"%s\"", responseStatusCode, buffer); #endif if(status == NO) { - [self doDisconnect:[self errorWithDetail:@"Invalid HTTP upgrade" code:1 userInfo:@{@"HTTPResponseStatusCode" : @(responseStatusCode)}]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"Invalid HTTP upgrade" code:1 userInfo:@{@"HTTPResponseStatusCode" : @(responseStatusCode)}]]; } } else { BOOL process = NO; @@ -447,7 +490,7 @@ - (BOOL)processHTTP:(uint8_t*)buffer length:(NSInteger)bufferLen responseStatusC dispatch_async(self.queue,^{ LCRTMWebSocket *strongSelf = weakSelf; if (!strongSelf) { return; } - if([strongSelf respondsToSelector:@selector(websocketDidConnect:)]) { + if([strongSelf.delegate respondsToSelector:@selector(websocketDidConnect:)]) { [strongSelf.delegate websocketDidConnect:strongSelf]; } }); @@ -513,18 +556,18 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { uint8_t payloadLen = (LCRTMPayloadLenMask & buffer[1]); NSInteger offset = 2; //how many bytes do we need to skip for the header if((isMasked || (LCRTMRSVMask & buffer[0])) && receivedOpcode != LCRTMOpCodePong) { - [self doDisconnect:[self errorWithDetail:@"masked and rsv data is not currently supported" code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"masked and rsv data is not currently supported" code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } BOOL isControlFrame = (receivedOpcode == LCRTMOpCodeConnectionClose || receivedOpcode == LCRTMOpCodePing); if(!isControlFrame && (receivedOpcode != LCRTMOpCodeBinaryFrame && receivedOpcode != LCRTMOpCodeContinueFrame && receivedOpcode != LCRTMOpCodeTextFrame && receivedOpcode != LCRTMOpCodePong)) { - [self doDisconnect:[self errorWithDetail:[NSString stringWithFormat:@"unknown opcode: 0x%x",receivedOpcode] code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:[NSString stringWithFormat:@"unknown opcode: 0x%x",receivedOpcode] code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } if(isControlFrame && !isFin) { - [self doDisconnect:[self errorWithDetail:@"control frames can't be fragmented" code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"control frames can't be fragmented" code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } @@ -553,7 +596,7 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { } } [self writeError:code]; - [self doDisconnect:[self errorWithDetail:@"continue frame before a binary or text frame" code:code]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"continue frame before a binary or text frame" code:code]]; return; } if(isControlFrame && payloadLen > 125) { @@ -604,14 +647,14 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { response = nil; //don't append pings } if(!isFin && receivedOpcode == LCRTMOpCodeContinueFrame && !response) { - [self doDisconnect:[self errorWithDetail:@"continue frame before a binary or text frame" code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"continue frame before a binary or text frame" code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } BOOL isNew = NO; if(!response) { if(receivedOpcode == LCRTMOpCodeContinueFrame) { - [self doDisconnect:[self errorWithDetail:@"first frame can't be a continue frame" code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"first frame can't be a continue frame" code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } @@ -624,7 +667,7 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { if(receivedOpcode == LCRTMOpCodeContinueFrame) { response.bytesLeft = dataLength; } else { - [self doDisconnect:[self errorWithDetail:@"second and beyond of fragment message must be a continue frame" code:LCRTMCloseCodeProtocolError]]; + [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"second and beyond of fragment message must be a continue frame" code:LCRTMCloseCodeProtocolError]]; [self writeError:LCRTMCloseCodeProtocolError]; return; } @@ -701,10 +744,10 @@ -(void)dequeueWrite:(NSData*)data withCode:(LCRTMOpCode)code { __weak typeof(self) weakSelf = self; [self.writeQueue addOperationWithBlock:^{ - if(!weakSelf || !weakSelf.isConnected) { + LCRTMWebSocket *strongSelf = weakSelf; + if(!strongSelf || !strongSelf.isConnected) { return; } - typeof(weakSelf) strongSelf = weakSelf; uint64_t offset = 2; //how many bytes do we need to skip for the header uint8_t *bytes = (uint8_t*)[data bytes]; uint64_t dataLength = data.length; @@ -748,7 +791,7 @@ -(void)dequeueWrite:(NSData*)data withCode:(LCRTMOpCode)code { if(len < 0 || len == NSNotFound) { NSError *error = strongSelf.outputStream.streamError; if(!error) { - error = [strongSelf errorWithDetail:@"output stream error during write" code:LCRTMOutputStreamWriteError]; + error = [LCRTMWebSocket errorWithDetail:@"output stream error during write" code:LCRTMOutputStreamWriteError]; } [strongSelf doDisconnect:error]; break; @@ -764,12 +807,14 @@ -(void)dequeueWrite:(NSData*)data withCode:(LCRTMOpCode)code { ///////////////////////////////////////////////////////////////////////////// - (void)doDisconnect:(NSError*)error { if(!self.didDisconnect) { + self.didDisconnect = YES; __weak typeof(self) weakSelf = self; dispatch_async(self.queue, ^{ LCRTMWebSocket *strongSelf = weakSelf; if (!strongSelf) { return; } - strongSelf.didDisconnect = YES; + [strongSelf disconnect]; + if([strongSelf.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) { [strongSelf.delegate websocketDidDisconnect:strongSelf error:error]; } @@ -777,18 +822,18 @@ - (void)doDisconnect:(NSError*)error { } } ///////////////////////////////////////////////////////////////////////////// -- (NSError*)errorWithDetail:(NSString*)detail code:(NSInteger)code -{ ++ (NSError *)errorWithDetail:(NSString *)detail code:(NSInteger)code { return [self errorWithDetail:detail code:code userInfo:nil]; } -- (NSError*)errorWithDetail:(NSString*)detail code:(NSInteger)code userInfo:(NSDictionary *)userInfo -{ - NSMutableDictionary* details = [NSMutableDictionary dictionary]; - details[detail] = NSLocalizedDescriptionKey; ++ (NSError *)errorWithDetail:(NSString *)detail code:(NSInteger)code userInfo:(NSDictionary *)userInfo { + NSMutableDictionary *info; if (userInfo) { - [details addEntriesFromDictionary:userInfo]; + info = [NSMutableDictionary dictionaryWithDictionary:userInfo]; + } else { + info = [NSMutableDictionary dictionary]; } - return [[NSError alloc] initWithDomain:@"LCRTMWebSocket" code:code userInfo:details]; + info[NSLocalizedFailureReasonErrorKey] = detail; + return [NSError errorWithDomain:@"LCRTMWebSocket" code:code userInfo:info]; } ///////////////////////////////////////////////////////////////////////////// - (void)writeError:(uint16_t)code { From bcd3661ccf7336675a4ae1e6fd608fb91f7dba53 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Wed, 5 Feb 2020 16:22:14 +0800 Subject: [PATCH 13/21] build: update CLI tool --- main.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main.swift b/main.swift index 77adadb32..422bac6c6 100755 --- a/main.swift +++ b/main.swift @@ -523,12 +523,15 @@ class AppledocTask: Task { try FileManager.default.removeItem(atPath: APIDocsTempDirectory) } - static func commitPush() throws { + static func commitPull() throws { guard GitTask(arguments: [ "-C", APIDocsRepoObjcDirectory, "pull"]) .excute() else { throw TaskError() } + } + + static func commitPush() throws { guard GitTask(arguments: [ "-C", APIDocsRepoObjcDirectory, "add", "-A"]) @@ -551,6 +554,7 @@ class AppledocTask: Task { static func update(currentVersion: VersionUpdater.Version) throws { try version() try checkAPIDocsRepoObjcDirectory() + try commitPull() try generateDocumentation(currentVersion: currentVersion) try moveGeneratedDocumentationToRepo() try commitPush() From 54acc595aae3eb84ff568b8110f2241037e9b927 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Wed, 18 Mar 2020 17:23:10 +0800 Subject: [PATCH 14/21] backup --- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index 056291798..754db58bd 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -856,3 +856,31 @@ @implementation LCRTMResponse @end ///////////////////////////////////////////////////////////////////////////// + +@interface LCRTMFoundationTransport : NSObject + +@property (nonatomic, strong) dispatch_queue_t workQueue; +@property (nonatomic, strong) NSInputStream *inputStream; +@property (nonatomic, strong) NSOutputStream *outputStream; +@property (nonatomic, assign) BOOL isOpened; + +@end + +@implementation LCRTMFoundationTransport + +- (instancetype)init +{ + self = [super init]; + if (self) { + _workQueue = dispatch_queue_create("LCRTMFoundationTransport.workQueue", NULL); + _isOpened = false; + } + return self; +} + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode +{ + +} + +@end From fe2c624c077e7bff0ae462b72ec074b3e66619d4 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Thu, 19 Mar 2020 17:33:28 +0800 Subject: [PATCH 15/21] backup --- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 106 ++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index 754db58bd..94a1cd92b 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -21,6 +21,8 @@ #import "LCRTMWebSocket.h" +#import "AVErrorUtils.h" + //get the opCode from the packet typedef NS_ENUM(NSUInteger, LCRTMOpCode) { LCRTMOpCodeContinueFrame = 0x0, @@ -857,9 +859,21 @@ @implementation LCRTMResponse @end ///////////////////////////////////////////////////////////////////////////// +@protocol LCRTMFoundationTransportDelegate + +- (void)connected; +- (void)cancelled; +- (void)failedWithError:(NSError *)error; +- (void)receive:(NSData *)data; + +@end + @interface LCRTMFoundationTransport : NSObject +@property (nonatomic, weak) id delegate; +@property (nonatomic, strong) dispatch_queue_t delegateQueue; @property (nonatomic, strong) dispatch_queue_t workQueue; +@property (nonatomic, strong) dispatch_queue_t writeQueue; @property (nonatomic, strong) NSInputStream *inputStream; @property (nonatomic, strong) NSOutputStream *outputStream; @property (nonatomic, assign) BOOL isOpened; @@ -872,12 +886,104 @@ - (instancetype)init { self = [super init]; if (self) { + _delegateQueue = dispatch_get_main_queue(); _workQueue = dispatch_queue_create("LCRTMFoundationTransport.workQueue", NULL); + _writeQueue = dispatch_queue_create("LCRTMFoundationTransport.writeQueue", NULL); _isOpened = false; } return self; } +- (void)connectURL:(NSURL *)URL timeout:(NSTimeInterval)timeout +{ + if (!URL.host) { + dispatch_async(self.delegateQueue, ^{ + [self.delegate failedWithError:LCError(9976, @"Invalid request.", @{ + @"URL": (URL ?: @"nil"), + })]; + }); + return; + } + NSNumber *port = URL.port; + if (!port) { + if (URL.scheme && [@[@"wss", @"https"] containsObject:URL.scheme]) { + port = @443; + } else { + port = @80; + } + } + CFReadStreamRef readStreamRef; + CFWriteStreamRef writeStreamRef; + CFStreamCreatePairWithSocketToHost(NULL, + (__bridge CFStringRef)(URL.host), + port.unsignedIntValue, + &readStreamRef, + &writeStreamRef); + self.inputStream = (__bridge_transfer NSInputStream *)(readStreamRef); + self.outputStream = (__bridge_transfer NSOutputStream *)(writeStreamRef); + if (!self.inputStream || !self.outputStream) { + dispatch_async(self.delegateQueue, ^{ + [self.delegate failedWithError:LCError(9976, @"Socket creating failed.", @{ + @"host": URL.host, + @"port": port, + })]; + }); + return; + } + self.inputStream.delegate = self; + self.outputStream.delegate = self; + self.isOpened = false; + CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, self.workQueue); + CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, self.workQueue); + [self.inputStream open]; + [self.outputStream open]; + __weak typeof(self) ws = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), self.workQueue, ^{ + LCRTMFoundationTransport *ss = ws; + if (!ss) { + return; + } + if (!ss.isOpened) { + dispatch_async(self.delegateQueue, ^{ + [ss.delegate failedWithError:LCError(9001, @"Socket opening timeout.", @{ + @"host": URL.host, + @"port": port, + })]; + }); + return; + } + }); +} + +- (void)disconnect +{ + dispatch_async(self.workQueue, ^{ + [self _disconnect]; + }); +} + +- (void)_disconnect +{ + if (self.inputStream) { + self.inputStream.delegate = nil; + CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, NULL); + [self.inputStream close]; + self.inputStream = nil; + } + if (self.outputStream) { + self.outputStream.delegate = nil; + CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, NULL); + [self.outputStream close]; + self.outputStream = nil; + } + self.isOpened = false; +} + +- (void)write:(NSData *)data error:(NSError * __autoreleasing *)errPtr +{ + +} + - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { From dd80c55d028bee9a4636ff87cfdc3133e02dd1b4 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 20 Mar 2020 16:16:27 +0800 Subject: [PATCH 16/21] fix(foundation): crash by multiple threads access close #603 --- AVOS/AVOSCloud/Query/AVQuery.m | 2 ++ AVOS/AVOSCloud/Request/AVPaasClient.h | 4 +++- AVOS/AVOSCloud/Request/AVPaasClient.m | 8 +++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AVOS/AVOSCloud/Query/AVQuery.m b/AVOS/AVOSCloud/Query/AVQuery.m index 3a5208b23..4b439c4ac 100644 --- a/AVOS/AVOSCloud/Query/AVQuery.m +++ b/AVOS/AVOSCloud/Query/AVQuery.m @@ -982,7 +982,9 @@ - (void)cancel Detail discussion: https://github.com/leancloud/paas/issues/828 We should deprecate this method in future. */ + [[AVPaasClient sharedInstance].lock lock]; NSMapTable *table = [[AVPaasClient sharedInstance].requestTable copy]; + [[AVPaasClient sharedInstance].lock unlock]; NSString *URLString = [[AVPaasClient sharedInstance] absoluteStringFromPath:[self queryPath] parameters:self.parameters]; for (NSString *key in table) { diff --git a/AVOS/AVOSCloud/Request/AVPaasClient.h b/AVOS/AVOSCloud/Request/AVPaasClient.h index 78c4dc00a..03a3993b8 100644 --- a/AVOS/AVOSCloud/Request/AVPaasClient.h +++ b/AVOS/AVOSCloud/Request/AVPaasClient.h @@ -41,8 +41,10 @@ FOUNDATION_EXPORT NSString *const LCHeaderFieldNameProduction; // only for cloud code yet @property (nonatomic, assign) BOOL productionMode; - @property (nonatomic, assign) BOOL isLastModifyEnabled; + +@property (nonatomic, strong) NSLock *lock; + -(void)clearLastModifyCache; - (AVACL *)updatedDefaultACL; diff --git a/AVOS/AVOSCloud/Request/AVPaasClient.m b/AVOS/AVOSCloud/Request/AVPaasClient.m index 2d4e4aca8..e80f95ba4 100644 --- a/AVOS/AVOSCloud/Request/AVPaasClient.m +++ b/AVOS/AVOSCloud/Request/AVPaasClient.m @@ -114,12 +114,7 @@ @interface AVPaasClient() @property (nonatomic, strong) LCURLSessionManager *sessionManager; -// The client is singleton, so the queue doesn't need release -#if OS_OBJECT_USE_OBJC @property (nonatomic, strong) dispatch_queue_t completionQueue; -#else -@property (nonatomic, assign) dispatch_queue_t completionQueue; -#endif @property (nonatomic, strong) NSMutableSet *runningArchivedRequests; @@ -166,6 +161,7 @@ - (instancetype)init { manager; }); + _lock = [[NSLock alloc] init]; } return self; @@ -678,7 +674,9 @@ - (void)performRequest:(NSURLRequest *)request dispatch_semaphore_signal(semaphore); }]; + [self.lock lock]; [self.requestTable setObject:dataTask forKey:request.URL.absoluteString]; + [self.lock unlock]; [dataTask resume]; From acf404013ef569690775d276b7e32efb67cf4a23 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 20 Mar 2020 16:25:07 +0800 Subject: [PATCH 17/21] chore: update brew and gem file --- Brewfile | 3 +-- Gemfile | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Gemfile diff --git a/Brewfile b/Brewfile index b5ddebc4c..0eb816516 100644 --- a/Brewfile +++ b/Brewfile @@ -1,4 +1,3 @@ brew 'hub' -brew 'cocoapods' brew 'appledoc' -brew 'protobuf' \ No newline at end of file +brew 'protobuf' diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..070dac019 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' do + gem 'cocoapods' +end From b01a2d9b9c71e27279f4d7dcaa339efb8564c42c Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 20 Mar 2020 16:27:53 +0800 Subject: [PATCH 18/21] release: 12.1.3 --- AVOS/AVOSCloud/UserAgent.h | 2 +- AVOSCloud.podspec | 2 +- AVOSCloudIM.podspec | 2 +- AVOSCloudLiveQuery.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AVOS/AVOSCloud/UserAgent.h b/AVOS/AVOSCloud/UserAgent.h index 52a2bf598..69de5196c 100644 --- a/AVOS/AVOSCloud/UserAgent.h +++ b/AVOS/AVOSCloud/UserAgent.h @@ -1 +1 @@ -#define SDK_VERSION @"12.1.2" +#define SDK_VERSION @"12.1.3" diff --git a/AVOSCloud.podspec b/AVOSCloud.podspec index ab88a15f4..0f4808eb8 100644 --- a/AVOSCloud.podspec +++ b/AVOSCloud.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloud' - s.version = '12.1.2' + s.version = '12.1.3' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud Objective-C SDK' s.authors = 'LeanCloud' diff --git a/AVOSCloudIM.podspec b/AVOSCloudIM.podspec index a04ae9e48..1c5763006 100644 --- a/AVOSCloudIM.podspec +++ b/AVOSCloudIM.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloudIM' - s.version = '12.1.2' + s.version = '12.1.3' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud IM Objective-C SDK' s.authors = 'LeanCloud' diff --git a/AVOSCloudLiveQuery.podspec b/AVOSCloudLiveQuery.podspec index 5fbbb798e..be646aaf1 100644 --- a/AVOSCloudLiveQuery.podspec +++ b/AVOSCloudLiveQuery.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloudLiveQuery' - s.version = '12.1.2' + s.version = '12.1.3' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud LiveQuery Objective-C SDK' s.authors = 'LeanCloud' From f235488668a14474476914f280a5cff2b1f78897 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Sun, 22 Mar 2020 17:42:58 +0800 Subject: [PATCH 19/21] feat(websocket): perfect reconstruction --- AVOS/AVOS.xcodeproj/project.pbxproj | 46 +- .../JNKeychain/AVKeychain.h | 0 .../JNKeychain/AVKeychain.m | 0 .../{ThirdParty => Vendor}/LCDB/LCDB.h | 0 .../{ThirdParty => Vendor}/LCDB/LCDatabase.h | 0 .../{ThirdParty => Vendor}/LCDB/LCDatabase.m | 0 .../LCDB/LCDatabaseAdditions.h | 0 .../LCDB/LCDatabaseAdditions.m | 0 .../LCDB/LCDatabasePool.h | 0 .../LCDB/LCDatabasePool.m | 0 .../LCDB/LCDatabaseQueue.h | 0 .../LCDB/LCDatabaseQueue.m | 0 .../{ThirdParty => Vendor}/LCDB/LCResultSet.h | 0 .../{ThirdParty => Vendor}/LCDB/LCResultSet.m | 0 .../LCNetworking/LCCompatibilityMacros.h | 0 .../LCNetworking/LCHTTPSessionManager.h | 0 .../LCNetworking/LCHTTPSessionManager.m | 0 .../LCNetworkReachabilityManager.h | 0 .../LCNetworkReachabilityManager.m | 0 .../LCNetworking/LCNetworking.h | 0 .../LCNetworking/LCSecurityPolicy.h | 0 .../LCNetworking/LCSecurityPolicy.m | 0 .../LCNetworking/LCURLRequestSerialization.h | 0 .../LCNetworking/LCURLRequestSerialization.m | 0 .../LCNetworking/LCURLResponseSerialization.h | 0 .../LCNetworking/LCURLResponseSerialization.m | 0 .../LCNetworking/LCURLSessionManager.h | 0 .../LCNetworking/LCURLSessionManager.m | 0 .../Vendor/SocketRocket/AVIMWebSocket.h | 125 -- .../Vendor/SocketRocket/AVIMWebSocket.m | 1779 ----------------- .../WebSocket/AVIMWebSocketWrapper.m | 113 +- AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h | 82 - AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m | 218 -- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h | 193 +- AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m | 1720 ++++++++-------- 35 files changed, 1004 insertions(+), 3272 deletions(-) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/JNKeychain/AVKeychain.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/JNKeychain/AVKeychain.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDB.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabase.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabase.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabaseAdditions.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabaseAdditions.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabasePool.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabasePool.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabaseQueue.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCDatabaseQueue.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCResultSet.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCDB/LCResultSet.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCCompatibilityMacros.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCHTTPSessionManager.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCHTTPSessionManager.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCNetworkReachabilityManager.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCNetworkReachabilityManager.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCNetworking.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCSecurityPolicy.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCSecurityPolicy.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLRequestSerialization.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLRequestSerialization.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLResponseSerialization.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLResponseSerialization.m (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLSessionManager.h (100%) rename AVOS/AVOSCloud/{ThirdParty => Vendor}/LCNetworking/LCURLSessionManager.m (100%) delete mode 100755 AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.h delete mode 100755 AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.m delete mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h delete mode 100644 AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m diff --git a/AVOS/AVOS.xcodeproj/project.pbxproj b/AVOS/AVOS.xcodeproj/project.pbxproj index 99f217578..3ed738ce6 100644 --- a/AVOS/AVOS.xcodeproj/project.pbxproj +++ b/AVOS/AVOS.xcodeproj/project.pbxproj @@ -421,10 +421,6 @@ 83A2BD331CF33CDA0047B230 /* AVPaasClient_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A2BD2E1CF33CDA0047B230 /* AVPaasClient_internal.h */; }; 83AB6DF81C3CFEA70064BE17 /* AVOSCloudIM.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AB6DF71C3CFEA70064BE17 /* AVOSCloudIM.m */; }; 83AB6DFA1C3CFEA70064BE17 /* AVOSCloudIM.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AB6DF71C3CFEA70064BE17 /* AVOSCloudIM.m */; }; - 83BE439A1D242905000C7AC3 /* AVIMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE43951D242905000C7AC3 /* AVIMWebSocket.h */; }; - 83BE439C1D242905000C7AC3 /* AVIMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE43951D242905000C7AC3 /* AVIMWebSocket.h */; }; - 83BE439D1D242905000C7AC3 /* AVIMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE43961D242905000C7AC3 /* AVIMWebSocket.m */; }; - 83BE439F1D242905000C7AC3 /* AVIMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE43961D242905000C7AC3 /* AVIMWebSocket.m */; }; 83CFCB601B2A5D2700F97C92 /* AVIMKeyedConversation.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CFCB5E1B2A5D2700F97C92 /* AVIMKeyedConversation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83CFCB611B2A5D2700F97C92 /* AVIMKeyedConversation.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CFCB5F1B2A5D2700F97C92 /* AVIMKeyedConversation.m */; }; 83D8CE751C17DAF50094279D /* AVRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C841A6D1A5A79FF00C5C6C4 /* AVRequestOperation.m */; }; @@ -771,14 +767,10 @@ D31126A7208F1C7A00812135 /* AVIMConversationMemberInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A2208F1C7900812135 /* AVIMConversationMemberInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; D31126A8208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */; }; D31126A9208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */; }; - D3120E0B23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */; }; - D3120E0C23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */; }; D3120E0D23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */; }; D3120E0E23B0DA8E00A64120 /* LCRTMWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */; }; D3120E0F23B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */; }; D3120E1023B0DA8E00A64120 /* LCRTMWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */; }; - D3120E1123B0DA8E00A64120 /* LCRTMSecurity.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */; }; - D3120E1223B0DA8E00A64120 /* LCRTMSecurity.m in Sources */ = {isa = PBXBuildFile; fileRef = D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */; }; D31F503F21194D1100123909 /* AVIMMessageTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31F503E21194D1100123909 /* AVIMMessageTestCase.swift */; }; D328B8E020FC84930039091A /* LCIMTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CDF4EA1FE8F3810033153E /* LCIMTestBase.swift */; }; D328B8E220FC85200039091A /* LCLiveQueryTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D328B8E120FC85200039091A /* LCLiveQueryTestBase.swift */; }; @@ -1174,8 +1166,6 @@ 838DD7A21B3D3FD100C95897 /* LCKeyValueSQL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCKeyValueSQL.h; sourceTree = ""; }; 83A2BD2E1CF33CDA0047B230 /* AVPaasClient_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVPaasClient_internal.h; sourceTree = ""; }; 83AB6DF71C3CFEA70064BE17 /* AVOSCloudIM.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVOSCloudIM.m; sourceTree = ""; }; - 83BE43951D242905000C7AC3 /* AVIMWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMWebSocket.h; sourceTree = ""; }; - 83BE43961D242905000C7AC3 /* AVIMWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVIMWebSocket.m; sourceTree = ""; }; 83CFCB5E1B2A5D2700F97C92 /* AVIMKeyedConversation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMKeyedConversation.h; sourceTree = ""; }; 83CFCB5F1B2A5D2700F97C92 /* AVIMKeyedConversation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVIMKeyedConversation.m; sourceTree = ""; }; 83D8CF551C17DAF50094279D /* AVOSCloud.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AVOSCloud.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1379,10 +1369,8 @@ D31126A1208F1C7900812135 /* AVIMConversationMemberInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVIMConversationMemberInfo.m; sourceTree = ""; }; D31126A2208F1C7900812135 /* AVIMConversationMemberInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMConversationMemberInfo.h; sourceTree = ""; }; D31126A3208F1C7A00812135 /* AVIMConversationMemberInfo_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVIMConversationMemberInfo_Internal.h; sourceTree = ""; }; - D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCRTMSecurity.h; sourceTree = ""; }; D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCRTMWebSocket.h; sourceTree = ""; }; D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCRTMWebSocket.m; sourceTree = ""; }; - D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCRTMSecurity.m; sourceTree = ""; }; D31F503E21194D1100123909 /* AVIMMessageTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVIMMessageTestCase.swift; sourceTree = ""; }; D328B8E120FC85200039091A /* LCLiveQueryTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCLiveQueryTestBase.swift; sourceTree = ""; }; D328B8E620FEE2200039091A /* AVIMClientInternalConversationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVIMClientInternalConversationManager.h; sourceTree = ""; }; @@ -1687,23 +1675,6 @@ path = Statistics; sourceTree = ""; }; - 83BE43911D242905000C7AC3 /* Vendor */ = { - isa = PBXGroup; - children = ( - 83BE43941D242905000C7AC3 /* SocketRocket */, - ); - path = Vendor; - sourceTree = ""; - }; - 83BE43941D242905000C7AC3 /* SocketRocket */ = { - isa = PBXGroup; - children = ( - 83BE43951D242905000C7AC3 /* AVIMWebSocket.h */, - 83BE43961D242905000C7AC3 /* AVIMWebSocket.m */, - ); - path = SocketRocket; - sourceTree = ""; - }; 83DF02A41AF86032000E289C /* LCDB */ = { isa = PBXGroup; children = ( @@ -1835,7 +1806,7 @@ 8C841A721A5A79FF00C5C6C4 /* Search */, 838DD7931B3D308100C95897 /* Statistics */, 8C841A771A5A7A0000C5C6C4 /* Status */, - 8C841A7A1A5A7A0000C5C6C4 /* ThirdParty */, + 8C841A7A1A5A7A0000C5C6C4 /* Vendor */, 8C841A9B1A5A7A0000C5C6C4 /* User */, 8C841AA11A5A7A0000C5C6C4 /* Utils */, 83331D421EB9A24D00CADC9C /* Captcha */, @@ -1872,7 +1843,6 @@ 838AF11D1AFE58F600B8965E /* MessageCache */, 8C9A92651A70AF1800CA4912 /* TypedMessages */, 8C841BFF1A5A84C600C5C6C4 /* Utilities */, - 83BE43911D242905000C7AC3 /* Vendor */, 8C841C041A5A84C600C5C6C4 /* WebSocket */, ); path = AVOSCloudIM; @@ -2049,14 +2019,14 @@ path = Status; sourceTree = ""; }; - 8C841A7A1A5A7A0000C5C6C4 /* ThirdParty */ = { + 8C841A7A1A5A7A0000C5C6C4 /* Vendor */ = { isa = PBXGroup; children = ( 83FCB9911CEDC57C007D8712 /* LCNetworking */, 836705DA1BA1C1710050E968 /* JNKeychain */, 83DF02A41AF86032000E289C /* LCDB */, ); - path = ThirdParty; + path = Vendor; sourceTree = ""; }; 8C841A9B1A5A7A0000C5C6C4 /* User */ = { @@ -2138,8 +2108,6 @@ 8C841C041A5A84C600C5C6C4 /* WebSocket */ = { isa = PBXGroup; children = ( - D3120E0723B0DA8E00A64120 /* LCRTMSecurity.h */, - D3120E0A23B0DA8E00A64120 /* LCRTMSecurity.m */, D3120E0823B0DA8E00A64120 /* LCRTMWebSocket.h */, D3120E0923B0DA8E00A64120 /* LCRTMWebSocket.m */, 8C841C091A5A84C600C5C6C4 /* AVIMWebSocketWrapper.h */, @@ -2450,7 +2418,6 @@ 704F3BC01BE0D07E0033245C /* AVIMConversation.h in Headers */, 704F3BC11BE0D07E0033245C /* AVIMKeyedConversation.h in Headers */, D3D6E4CA23544F590048E58F /* LCGPBUnknownField.h in Headers */, - 83BE439C1D242905000C7AC3 /* AVIMWebSocket.h in Headers */, 830F15A11F010F260009B4D0 /* AVIMRecalledMessage.h in Headers */, D3D6E4D423544F590048E58F /* LCGPBExtensionInternals.h in Headers */, 704F3BC41BE0D07E0033245C /* AVIMConversationQuery.h in Headers */, @@ -2464,7 +2431,6 @@ D3D6E4E623544F590048E58F /* LCGPBType.pbobjc.h in Headers */, D3D6E4E223544F590048E58F /* LCGPBCodedOutputStream.h in Headers */, 704F3BF41BE0D0820033245C /* AVIMImageMessage.h in Headers */, - D3120E0C23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */, D3D6E4DA23544F590048E58F /* LCGPBDescriptor_PackagePrivate.h in Headers */, D3939CC820FEE621001C9F5C /* AVIMClientInternalConversationManager_Internal.h in Headers */, 704F3BF51BE0D0820033245C /* AVIMLocationMessage.h in Headers */, @@ -2947,7 +2913,6 @@ D3D6E4A723544F590048E58F /* LCGPBUnknownFieldSet.h in Headers */, 8CBDFDE21A808EA500D67FBE /* AVIMConversationQuery.h in Headers */, 8C9A92781A70AF1800CA4912 /* AVIMTextMessage.h in Headers */, - 83BE439A1D242905000C7AC3 /* AVIMWebSocket.h in Headers */, D3D6E4C923544F590048E58F /* LCGPBUnknownField.h in Headers */, 830F15A01F010F260009B4D0 /* AVIMRecalledMessage.h in Headers */, 8C9A92761A70AF1800CA4912 /* AVIMLocationMessage.h in Headers */, @@ -2963,7 +2928,6 @@ D3D6E4E523544F590048E58F /* LCGPBType.pbobjc.h in Headers */, D3D6E4E123544F590048E58F /* LCGPBCodedOutputStream.h in Headers */, 8C9A92741A70AF1800CA4912 /* AVIMImageMessage.h in Headers */, - D3120E0B23B0DA8E00A64120 /* LCRTMSecurity.h in Headers */, D3D6E4D923544F590048E58F /* LCGPBDescriptor_PackagePrivate.h in Headers */, 839A35671D2A1DC000B3AF1D /* AVIMMessage_Internal.h in Headers */, 9AD392461BFC411300D28074 /* AVIMGenericCommand+AVIMMessagesAdditions.h in Headers */, @@ -3492,7 +3456,6 @@ files = ( D3D6E48223544F590048E58F /* LCGPBTimestamp.pbobjc.m in Sources */, 704F3C061BE0D5650033245C /* avmp.c in Sources */, - D3120E1223B0DA8E00A64120 /* LCRTMSecurity.m in Sources */, 704F3B851BE0D05C0033245C /* AVIMClient.m in Sources */, 704F3B861BE0D05C0033245C /* AVIMCommon.m in Sources */, D328B8EB20FEE2200039091A /* AVIMClientInternalConversationManager.m in Sources */, @@ -3536,7 +3499,6 @@ 704F3BAD1BE0D05C0033245C /* LCIMConversationCacheStore.m in Sources */, 704F3BAE1BE0D05C0033245C /* LCIMConversationQueryCacheStore.m in Sources */, D33B830D210F1679001D1CCD /* AVIMClientPushManager.m in Sources */, - 83BE439F1D242905000C7AC3 /* AVIMWebSocket.m in Sources */, 704F3BAF1BE0D05C0033245C /* AVIMAudioMessage.m in Sources */, D3D6E4B623544F590048E58F /* LCGPBDuration.pbobjc.m in Sources */, 704F3BB01BE0D05C0033245C /* AVIMImageMessage.m in Sources */, @@ -3872,7 +3834,6 @@ files = ( D3D6E48123544F590048E58F /* LCGPBTimestamp.pbobjc.m in Sources */, 83F745F21B91732D00437259 /* LCIMMessageCacheStore.m in Sources */, - D3120E1123B0DA8E00A64120 /* LCRTMSecurity.m in Sources */, D328B8EA20FEE2200039091A /* AVIMClientInternalConversationManager.m in Sources */, 8C2FED551A67C2DA0056F945 /* AVIMGeneralObject.m in Sources */, 8C9A92731A70AF1800CA4912 /* AVIMAudioMessage.m in Sources */, @@ -3914,7 +3875,6 @@ D3D6E4A523544F590048E58F /* LCGPBDescriptor.m in Sources */, 8C841C121A5A84C600C5C6C4 /* AVIMConversation.m in Sources */, D33B830C210F1679001D1CCD /* AVIMClientPushManager.m in Sources */, - 83BE439D1D242905000C7AC3 /* AVIMWebSocket.m in Sources */, 83AB6DF81C3CFEA70064BE17 /* AVOSCloudIM.m in Sources */, 8C9A92791A70AF1800CA4912 /* AVIMTextMessage.m in Sources */, 83F745F71B91735B00437259 /* LCIMCacheStore.m in Sources */, diff --git a/AVOS/AVOSCloud/ThirdParty/JNKeychain/AVKeychain.h b/AVOS/AVOSCloud/Vendor/JNKeychain/AVKeychain.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/JNKeychain/AVKeychain.h rename to AVOS/AVOSCloud/Vendor/JNKeychain/AVKeychain.h diff --git a/AVOS/AVOSCloud/ThirdParty/JNKeychain/AVKeychain.m b/AVOS/AVOSCloud/Vendor/JNKeychain/AVKeychain.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/JNKeychain/AVKeychain.m rename to AVOS/AVOSCloud/Vendor/JNKeychain/AVKeychain.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDB.h b/AVOS/AVOSCloud/Vendor/LCDB/LCDB.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDB.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCDB.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabase.h b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabase.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabase.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabase.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabase.m b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabase.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabase.m rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabase.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseAdditions.h b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseAdditions.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseAdditions.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseAdditions.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseAdditions.m b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseAdditions.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseAdditions.m rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseAdditions.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabasePool.h b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabasePool.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabasePool.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabasePool.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabasePool.m b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabasePool.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabasePool.m rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabasePool.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseQueue.h b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseQueue.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseQueue.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseQueue.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseQueue.m b/AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseQueue.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCDatabaseQueue.m rename to AVOS/AVOSCloud/Vendor/LCDB/LCDatabaseQueue.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCResultSet.h b/AVOS/AVOSCloud/Vendor/LCDB/LCResultSet.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCResultSet.h rename to AVOS/AVOSCloud/Vendor/LCDB/LCResultSet.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCDB/LCResultSet.m b/AVOS/AVOSCloud/Vendor/LCDB/LCResultSet.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCDB/LCResultSet.m rename to AVOS/AVOSCloud/Vendor/LCDB/LCResultSet.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCCompatibilityMacros.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCCompatibilityMacros.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCCompatibilityMacros.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCHTTPSessionManager.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCHTTPSessionManager.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCHTTPSessionManager.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCHTTPSessionManager.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCHTTPSessionManager.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworkReachabilityManager.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworkReachabilityManager.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworkReachabilityManager.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworkReachabilityManager.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworkReachabilityManager.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworking.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCNetworking.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCNetworking.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCSecurityPolicy.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCSecurityPolicy.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCSecurityPolicy.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCSecurityPolicy.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCSecurityPolicy.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLRequestSerialization.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLRequestSerialization.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLRequestSerialization.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLRequestSerialization.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLRequestSerialization.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLResponseSerialization.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLResponseSerialization.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLResponseSerialization.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLResponseSerialization.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLResponseSerialization.m diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLSessionManager.h similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.h rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLSessionManager.h diff --git a/AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m b/AVOS/AVOSCloud/Vendor/LCNetworking/LCURLSessionManager.m similarity index 100% rename from AVOS/AVOSCloud/ThirdParty/LCNetworking/LCURLSessionManager.m rename to AVOS/AVOSCloud/Vendor/LCNetworking/LCURLSessionManager.m diff --git a/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.h b/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.h deleted file mode 100755 index d41ddd246..000000000 --- a/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.h +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -typedef NS_ENUM(NSUInteger, AVIMWebSocketState) { - AVIMWebSocketStateNone = 0, - AVIMWebSocketStateConnecting = 1, - AVIMWebSocketStateConnected = 2, - AVIMWebSocketStateClosing = 3, - AVIMWebSocketStateClosed = 4, -}; - -typedef enum AVIMStatusCode : NSInteger { - AVIMStatusCodeNormal = 1000, - AVIMStatusCodeGoingAway = 1001, - AVIMStatusCodeProtocolError = 1002, - AVIMStatusCodeUnhandledType = 1003, - // 1004 reserved. - AVIMStatusNoStatusReceived = 1005, - // 1004-1006 reserved. - AVIMStatusCodeInvalidUTF8 = 1007, - AVIMStatusCodePolicyViolated = 1008, - AVIMStatusCodeMessageTooBig = 1009, -} AVIMStatusCode; - -@class AVIMWebSocket; - -extern NSString *const AVIMWebSocketErrorDomain; -extern NSString *const AVIMHTTPResponseErrorKey; - -#pragma mark - AVIMWebSocketDelegate - -@protocol AVIMWebSocketDelegate; - -#pragma mark - AVIMWebSocket - -@interface AVIMWebSocket : NSObject - -@property (nonatomic, weak) id delegate; - -@property (nonatomic, readonly) AVIMWebSocketState readyState; -@property (nonatomic, readonly, retain) NSURL *url; - - -@property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders; - -// Optional array of cookies (NSHTTPCookie objects) to apply to the connections -@property (nonatomic, readwrite) NSArray * requestCookies; - -// This returns the negotiated protocol. -// It will be nil until after the handshake completes. -@property (nonatomic, readonly, copy) NSString *protocol; - -// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; -- (id)initWithURLRequest:(NSURLRequest *)request; - -// Some helper constructors. -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; -- (id)initWithURL:(NSURL *)url; - -// Delegate queue will be dispatch_main_queue by default. -// You cannot set both OperationQueue and dispatch_queue. -- (void)setDelegateOperationQueue:(NSOperationQueue*) queue; -- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; - -// By default, it will schedule itself on +[NSRunLoop AVIM_networkRunLoop] using defaultModes. -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; -- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; - -// AVIMWebSockets are intended for one-time-use only. Open should be called once and only once. -- (void)open; - -- (void)close; -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; - -// Send a UTF8 String or Data. -- (void)send:(id)data; - -// Send Data (can be nil) in a ping message. -- (void)sendPing:(NSData *)data; - -@end - -#pragma mark - AVIMWebSocketDelegate - -@protocol AVIMWebSocketDelegate - -// message will either be an NSString if the server is using text -// or NSData if the server is using binary. -- (void)webSocket:(AVIMWebSocket *)webSocket didReceiveMessage:(id)message; - -@optional - -- (void)webSocketDidOpen:(AVIMWebSocket *)webSocket; -- (void)webSocket:(AVIMWebSocket *)webSocket didFailWithError:(NSError *)error; -- (void)webSocket:(AVIMWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; -- (void)webSocket:(AVIMWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; - -@end - -#pragma mark - NSRunLoop (AVIMWebSocket) - -@interface NSRunLoop (AVIMWebSocket) - -+ (NSRunLoop *)AVIM_networkRunLoop; - -@end diff --git a/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.m b/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.m deleted file mode 100755 index 65cb348c1..000000000 --- a/AVOS/AVOSCloudIM/Vendor/SocketRocket/AVIMWebSocket.m +++ /dev/null @@ -1,1779 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - - -#import "AVIMWebSocket.h" - -#if TARGET_OS_IPHONE -#define HAS_ICU -#endif - -#ifdef HAS_ICU -#import -#endif - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -#import -#import - -#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE -#define avim_dispatch_retain(x) -#define avim_dispatch_release(x) -#define maybe_bridge(x) ((__bridge void *) x) -#else -#define avim_dispatch_retain(x) dispatch_retain(x) -#define avim_dispatch_release(x) dispatch_release(x) -#define maybe_bridge(x) (x) -#endif - -#if !__has_feature(objc_arc) -#error AVOSCloudIM must be compiled with ARC enabled -#endif - - -typedef enum { - AVIMOpCodeTextFrame = 0x1, - AVIMOpCodeBinaryFrame = 0x2, - // 3-7 reserved. - AVIMOpCodeConnectionClose = 0x8, - AVIMOpCodePing = 0x9, - AVIMOpCodePong = 0xA, - // B-F reserved. -} AVIMOpCode; - -typedef struct { - BOOL fin; -// BOOL rsv1; -// BOOL rsv2; -// BOOL rsv3; - uint8_t opcode; - BOOL masked; - uint64_t payload_length; -} frame_header; - -static NSString *const AVIMWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -static inline int32_t validate_dispatch_data_partial_string(NSData *data); -static inline void AVIMFastLog(NSString *format, ...); - -@interface NSData (AVIMWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; - -@end - - -@interface NSString (AVIMWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; - -@end - - -@interface NSURL (AVIMWebSocket) - -// The origin isn't really applicable for a native application. -// So instead, just map ws -> http and wss -> https. -- (NSString *)AVIM_origin; - -@end - - -@interface _AVIMRunLoopThread : NSThread - -@property (nonatomic, readonly) NSRunLoop *runLoop; - -@end - - -static NSString *newSHA1String(const char *bytes, size_t length) { - uint8_t md[CC_SHA1_DIGEST_LENGTH]; - - assert(length >= 0); - assert(length <= UINT32_MAX); - CC_SHA1(bytes, (CC_LONG)length, md); - - NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; - - if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { - return [data base64EncodedStringWithOptions:0]; - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [data base64Encoding]; -#pragma clang diagnostic pop -} - -@implementation NSData (AVIMWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; -{ - return newSHA1String(self.bytes, self.length); -} - -@end - - -@implementation NSString (AVIMWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; -{ - return newSHA1String(self.UTF8String, self.length); -} - -@end - -NSString *const AVIMWebSocketErrorDomain = @"AVIMWebSocketErrorDomain"; -NSString *const AVIMHTTPResponseErrorKey = @"HTTPResponseStatusCode"; - -// Returns number of bytes consumed. Returning 0 means you didn't match. -// Sends bytes to callback handler; -typedef size_t (^stream_scanner)(NSData *collected_data); - -typedef void (^data_callback)(AVIMWebSocket *webSocket, NSData *data); - -@interface AVIMIOConsumer : NSObject { - stream_scanner _scanner; - data_callback _handler; - size_t _bytesNeeded; - BOOL _readToCurrentFrame; - BOOL _unmaskBytes; -} -@property (nonatomic, copy, readonly) stream_scanner consumer; -@property (nonatomic, copy, readonly) data_callback handler; -@property (nonatomic, assign) size_t bytesNeeded; -@property (nonatomic, assign, readonly) BOOL readToCurrentFrame; -@property (nonatomic, assign, readonly) BOOL unmaskBytes; - -@end - -// This class is not thread-safe, and is expected to always be run on the same queue. -@interface AVIMIOConsumerPool : NSObject - -- (id)initWithBufferCapacity:(NSUInteger)poolSize; - -- (AVIMIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -- (void)returnConsumer:(AVIMIOConsumer *)consumer; - -@end - -@interface AVIMWebSocket () - -@property (nonatomic) AVIMWebSocketState readyState; - -@property (nonatomic) NSOperationQueue *delegateOperationQueue; -@property (nonatomic) dispatch_queue_t delegateDispatchQueue; - -// Specifies whether SSL trust chain should NOT be evaluated. -// By default this flag is set to NO, meaning only secure SSL connections are allowed. -// For DEBUG builds this flag is ignored, and SSL connections are allowed regardless -// of the certificate trust configuration -@property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates; - -@end - - -@implementation AVIMWebSocket { - NSInteger _webSocketVersion; - - NSOperationQueue *_delegateOperationQueue; - dispatch_queue_t _delegateDispatchQueue; - - dispatch_queue_t _workQueue; - NSMutableArray *_consumers; - - NSInputStream *_inputStream; - NSOutputStream *_outputStream; - - NSMutableData *_readBuffer; - NSUInteger _readBufferOffset; - - NSMutableData *_outputBuffer; - NSUInteger _outputBufferOffset; - - uint8_t _currentFrameOpcode; - size_t _currentFrameCount; - size_t _readOpCount; - uint32_t _currentStringScanPosition; - NSMutableData *_currentFrameData; - - NSString *_closeReason; - - NSString *_secKey; - NSString *_basicAuthorizationString; - - uint8_t _currentReadMaskKey[4]; - size_t _currentReadMaskOffset; - - BOOL _consumerStopped; - - BOOL _closeWhenFinishedWriting; - BOOL _failed; - - BOOL _secure; - NSURLRequest *_urlRequest; - - BOOL _sentClose; - - int _closeCode; - - BOOL _isPumping; - - NSMutableSet *_scheduledRunloops; - - // We use this to retain ourselves. - __strong AVIMWebSocket *_selfRetain; - - NSArray *_requestedProtocols; - AVIMIOConsumerPool *_consumerPool; -} - -@synthesize delegate = _delegate; -@synthesize url = _url; -@synthesize readyState = _readyState; -@synthesize protocol = _protocol; - -static __strong NSData *CRLFCRLF; - -+ (void)initialize; -{ - CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; -} - -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; -{ - self = [super init]; - if (self) { - assert(request.URL); - _url = request.URL; - _urlRequest = request; - _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates; - - _requestedProtocols = [protocols copy]; - - [self _AVIM_commonInit]; - } - - return self; -} - -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; -{ - return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO]; -} - -- (id)initWithURLRequest:(NSURLRequest *)request; -{ - return [self initWithURLRequest:request protocols:nil]; -} - -- (id)initWithURL:(NSURL *)url; -{ - return [self initWithURL:url protocols:nil]; -} - -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; -{ - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - return [self initWithURLRequest:request protocols:protocols]; -} - -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; -{ - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates]; -} - -- (void)_AVIM_commonInit; -{ - - NSString *scheme = _url.scheme.lowercaseString; - assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); - - if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { - _secure = YES; - } - - _readyState = AVIMWebSocketStateNone; - _consumerStopped = YES; - _webSocketVersion = 13; - - _workQueue = dispatch_queue_create("AVIMWebSocket._workQueue", DISPATCH_QUEUE_SERIAL); - - // Going to set a specific on the queue so we can validate we're on the work queue - dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); - - _delegateDispatchQueue = dispatch_get_main_queue(); - avim_dispatch_retain(_delegateDispatchQueue); - - _readBuffer = [[NSMutableData alloc] init]; - _outputBuffer = [[NSMutableData alloc] init]; - - _currentFrameData = [[NSMutableData alloc] init]; - - _consumers = [[NSMutableArray alloc] init]; - - _consumerPool = [[AVIMIOConsumerPool alloc] init]; - - _scheduledRunloops = [[NSMutableSet alloc] init]; - - [self _initializeStreams]; - - // default handlers -} - -- (void)assertOnWorkQueue; -{ - assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); -} - -- (void)dealloc -{ - _inputStream.delegate = nil; - _outputStream.delegate = nil; - - [_inputStream close]; - [_outputStream close]; - - if (_workQueue) { - avim_dispatch_release(_workQueue); - _workQueue = NULL; - } - - if (_receivedHTTPHeaders) { - CFRelease(_receivedHTTPHeaders); - _receivedHTTPHeaders = NULL; - } - - if (_delegateDispatchQueue) { - avim_dispatch_release(_delegateDispatchQueue); - _delegateDispatchQueue = NULL; - } -} - -#ifndef NDEBUG - -- (void)setReadyState:(AVIMWebSocketState)aReadyState; -{ - NSAssert(aReadyState > _readyState, - @"Error in change state"); - - [self willChangeValueForKey:@"readyState"]; - - _readyState = aReadyState; - - [self didChangeValueForKey:@"readyState"]; -} - -#endif - -- (void)open; -{ - assert(_url); - NSAssert(_readyState == AVIMWebSocketStateNone, @"Cannot call -(void)open on AVIMWebSocket more than once"); - - [self setReadyState:AVIMWebSocketStateConnecting]; - - _selfRetain = self; - - [self openConnection]; -} - -// Calls block on delegate queue -- (void)_performDelegateBlock:(dispatch_block_t)block; -{ - if (_delegateOperationQueue) { - [_delegateOperationQueue addOperationWithBlock:block]; - } else { - assert(_delegateDispatchQueue); - dispatch_async(_delegateDispatchQueue, block); - } -} - -- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; -{ - if (queue) { - avim_dispatch_retain(queue); - } - - if (_delegateDispatchQueue) { - avim_dispatch_release(_delegateDispatchQueue); - } - - _delegateDispatchQueue = queue; -} - -- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; -{ - NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); - - if (acceptHeader == nil) { - return NO; - } - - NSString *concattedString = [_secKey stringByAppendingString:AVIMWebSocketAppendToSecKeyString]; - NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; - - return [acceptHeader isEqualToString:expectedAccept]; -} - -- (void)_HTTPHeadersDidFinish; -{ - NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); - - if (responseCode >= 400) { - AVIMFastLog(@"Request failed with response code %d", responseCode); - [self _failWithError:[NSError errorWithDomain:AVIMWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], AVIMHTTPResponseErrorKey:@(responseCode)}]]; - return; - } - - if(![self _checkHandshake:_receivedHTTPHeaders]) { - [self _failWithError:[NSError errorWithDomain:AVIMWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; - return; - } - - NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); - if (negotiatedProtocol) { - // Make sure we requested the protocol - if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { - [self _failWithError:[NSError errorWithDomain:AVIMWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; - return; - } - - _protocol = negotiatedProtocol; - } - - [self setReadyState:AVIMWebSocketStateConnected]; - - [self _readFrameNew]; - - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate && [delegate respondsToSelector:@selector(webSocketDidOpen:)]) { - [delegate webSocketDidOpen:self]; - }; - }]; -} - - -- (void)_readHTTPHeader; -{ - if (_receivedHTTPHeaders == NULL) { - _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); - } - - [self _readUntilHeaderCompleteWithCallback:^(AVIMWebSocket *self, NSData *data) { - CFHTTPMessageAppendBytes(self->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); - - if (CFHTTPMessageIsHeaderComplete(self->_receivedHTTPHeaders)) { - AVIMFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_receivedHTTPHeaders))); - [self _HTTPHeadersDidFinish]; - } else { - [self _readHTTPHeader]; - } - }]; -} - -- (void)didConnect -{ - AVIMFastLog(@"Connected"); - CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); - - // Set host first so it defaults - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); - - NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-result" - SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); -#pragma clang diagnostic pop - - if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { - _secKey = [keyBytes base64EncodedStringWithOptions:0]; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _secKey = [keyBytes base64Encoding]; -#pragma clang diagnostic pop - } - - assert([_secKey length] == 24); - - // Apply cookies if any have been provided - NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]]; - for (NSString * cookieKey in cookies) { - NSString * cookieValue = [cookies objectForKey:cookieKey]; - if ([cookieKey length] && [cookieValue length]) { - CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue); - } - } - - // set header for http basic auth - if (_url.user.length && _url.password.length) { - NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *userAndPasswordBase64Encoded; - if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { - userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0]; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - userAndPasswordBase64Encoded = [userAndPassword base64Encoding]; -#pragma clang diagnostic pop - } - _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded]; - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString); - } - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.AVIM_origin); - - if (_requestedProtocols) { - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); - } - - [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); - }]; - - NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); - - CFRelease(request); - - [self _writeData:message]; - [self _readHTTPHeader]; -} - -- (void)_initializeStreams; -{ - assert(_url.port.unsignedIntValue <= UINT32_MAX); - uint32_t port = _url.port.unsignedIntValue; - if (port == 0) { - if (!_secure) { - port = 80; - } else { - port = 443; - } - } - NSString *host = _url.host; - - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - - CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); - - _outputStream = CFBridgingRelease(writeStream); - _inputStream = CFBridgingRelease(readStream); - - _inputStream.delegate = self; - _outputStream.delegate = self; -} - -- (void)_updateSecureStreamOptions; -{ - if (_secure) { - NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; - - [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; - - /* - * NOTE: we (LeanCloud) disable the certificate validation explicitly for using IP access. - */ - [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; - - [_outputStream setProperty:SSLOptions - forKey:(__bridge id)kCFStreamPropertySSLSettings]; - } -} - -- (void)openConnection; -{ - [self _updateSecureStreamOptions]; - - if (!_scheduledRunloops.count) { - [self scheduleInRunLoop:[NSRunLoop AVIM_networkRunLoop] forMode:NSDefaultRunLoopMode]; - } - - - [_outputStream open]; - [_inputStream open]; -} - -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; -{ - [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; - [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; - - [_scheduledRunloops addObject:@[aRunLoop, mode]]; -} - -- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; -{ - [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; - [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; - - [_scheduledRunloops removeObject:@[aRunLoop, mode]]; -} - -- (void)close; -{ - [self closeWithCode:AVIMStatusCodeNormal reason:nil]; -} - -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; -{ - assert(code); - dispatch_async(_workQueue, ^{ - if (self.readyState == AVIMWebSocketStateClosing || self.readyState == AVIMWebSocketStateClosed) { - return; - } - - BOOL wasConnecting = self.readyState == AVIMWebSocketStateConnecting; - - [self setReadyState:AVIMWebSocketStateClosing]; - - AVIMFastLog(@"Closing with code %d reason %@", code, reason); - - if (wasConnecting) { - [self closeConnection]; - return; - } - - size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; - NSData *payload = mutablePayload; - - ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); - - if (reason) { - NSRange remainingRange = {0}; - - NSUInteger usedLength = 0; - - 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]; - #pragma unused (success) - - assert(success); - assert(remainingRange.length == 0); - - if (usedLength != maxMsgSize) { - payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; - } - } - - - [self _sendFrameWithOpcode:AVIMOpCodeConnectionClose data:payload]; - }); -} - -- (void)_closeWithProtocolError:(NSString *)message; -{ - // Need to shunt this on the _callbackQueue first to see if they received any messages - [self _performDelegateBlock:^{ - [self closeWithCode:AVIMStatusCodeProtocolError reason:message]; - dispatch_async(self->_workQueue, ^{ - [self closeConnection]; - }); - }]; -} - -- (void)_failWithError:(NSError *)error; -{ - dispatch_async(_workQueue, ^{ - - if (self.readyState != AVIMWebSocketStateClosed) { - - [self setReadyState:AVIMWebSocketStateClosed]; - - self->_failed = YES; - - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate && [delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { - [delegate webSocket:self didFailWithError:error]; - } - }]; - - self->_selfRetain = nil; - - AVIMFastLog(@"Failing with error %@", error.localizedDescription); - - [self closeConnection]; - } - }); -} - -- (void)_writeData:(NSData *)data; -{ - [self assertOnWorkQueue]; - - if (_closeWhenFinishedWriting) { - return; - } - [_outputBuffer appendData:data]; - [self _pumpWriting]; -} - -- (void)send:(id)data; -{ - NSAssert((self.readyState != AVIMWebSocketStateConnecting) && - (self.readyState != AVIMWebSocketStateNone), - @"Invalid State: Cannot call send: until connection is open"); - // TODO: maybe not copy this for performance - data = [data copy]; - dispatch_async(_workQueue, ^{ - if ([data isKindOfClass:[NSString class]]) { - [self _sendFrameWithOpcode:AVIMOpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; - } else if ([data isKindOfClass:[NSData class]]) { - [self _sendFrameWithOpcode:AVIMOpCodeBinaryFrame data:data]; - } else if (data == nil) { - [self _sendFrameWithOpcode:AVIMOpCodeTextFrame data:data]; - } else { - assert(NO); - } - }); -} - -- (void)sendPing:(NSData *)data; -{ - NSAssert((self.readyState != AVIMWebSocketStateConnecting) && - (self.readyState != AVIMWebSocketStateNone), - @"Invalid State: Cannot call send: until connection is open"); - // TODO: maybe not copy this for performance - data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty - dispatch_async(_workQueue, ^{ - [self _sendFrameWithOpcode:AVIMOpCodePing data:data]; - }); -} - -- (void)handlePing:(NSData *)pingData; -{ - // Need to pingpong this off _callbackQueue first to make sure messages happen in order - [self _performDelegateBlock:^{ - dispatch_async(self->_workQueue, ^{ - [self _sendFrameWithOpcode:AVIMOpCodePong data:pingData]; - }); - }]; -} - -- (void)handlePong:(NSData *)pongData; -{ - AVIMFastLog(@"Received pong"); - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate && [delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) { - [delegate webSocket:self didReceivePong:pongData]; - } - }]; -} - -- (void)_handleMessage:(id)message -{ - AVIMFastLog(@"Received message"); - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate) { - [delegate webSocket:self didReceiveMessage:message]; - } - }]; -} - - -static inline BOOL closeCodeIsValid(int closeCode) { - if (closeCode < 1000) { - return NO; - } - - if (closeCode >= 1000 && closeCode <= 1011) { - if (closeCode == 1004 || - closeCode == 1005 || - closeCode == 1006) { - return NO; - } - return YES; - } - - if (closeCode >= 3000 && closeCode <= 3999) { - return YES; - } - - if (closeCode >= 4000 && closeCode <= 4999) { - return YES; - } - - return NO; -} - -// Note from RFC: -// -// If there is a body, the first two -// bytes of the body MUST be a 2-byte unsigned integer (in network byte -// order) representing a status code with value /code/ defined in -// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 -// encoded data with value /reason/, the interpretation of which is not -// defined by this specification. - -- (void)handleCloseWithData:(NSData *)data; -{ - size_t dataSize = data.length; - __block uint16_t closeCode = 0; - - AVIMFastLog(@"Received close frame"); - - if (dataSize == 1) { - // TODO handle error - [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; - return; - } else if (dataSize >= 2) { - [data getBytes:&closeCode length:sizeof(closeCode)]; - _closeCode = EndianU16_BtoN(closeCode); - if (!closeCodeIsValid(_closeCode)) { - [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; - return; - } - if (dataSize > 2) { - _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; - if (!_closeReason) { - [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; - return; - } - } - } else { - _closeCode = AVIMStatusNoStatusReceived; - } - - [self assertOnWorkQueue]; - - if (self.readyState == AVIMWebSocketStateConnected) { - [self closeWithCode:1000 reason:nil]; - } - dispatch_async(_workQueue, ^{ - [self closeConnection]; - }); -} - -- (void)closeConnection; -{ - [self assertOnWorkQueue]; - AVIMFastLog(@"Trying to disconnect"); - _closeWhenFinishedWriting = YES; - [self _pumpWriting]; -} - -- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; -{ - // Check that the current data is valid UTF8 - - BOOL isControlFrame = (opcode == AVIMOpCodePing || opcode == AVIMOpCodePong || opcode == AVIMOpCodeConnectionClose); - if (!isControlFrame) { - [self _readFrameNew]; - } else { - dispatch_async(_workQueue, ^{ - [self _readFrameContinue]; - }); - } - - switch (opcode) { - case AVIMOpCodeTextFrame: { - NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; - if (str == nil && frameData) { - [self closeWithCode:AVIMStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self closeConnection]; - }); - - return; - } - [self _handleMessage:str]; - break; - } - case AVIMOpCodeBinaryFrame: - [self _handleMessage:[frameData copy]]; - break; - case AVIMOpCodeConnectionClose: - [self handleCloseWithData:frameData]; - break; - case AVIMOpCodePing: - [self handlePing:frameData]; - break; - case AVIMOpCodePong: - [self handlePong:frameData]; - break; - default: - [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; - // TODO: Handle invalid opcode - break; - } -} - -- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; -{ - assert(frame_header.opcode != 0); - - if (self.readyState != AVIMWebSocketStateConnected) { - return; - } - - - BOOL isControlFrame = (frame_header.opcode == AVIMOpCodePing || frame_header.opcode == AVIMOpCodePong || frame_header.opcode == AVIMOpCodeConnectionClose); - - if (isControlFrame && !frame_header.fin) { - [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; - return; - } - - if (isControlFrame && frame_header.payload_length >= 126) { - [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; - return; - } - - if (!isControlFrame) { - _currentFrameOpcode = frame_header.opcode; - _currentFrameCount += 1; - } - - if (frame_header.payload_length == 0) { - if (isControlFrame) { - [self _handleFrameWithData:curData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO add assert that opcode is not a control; - [self _readFrameContinue]; - } - } - } else { - assert(frame_header.payload_length <= SIZE_T_MAX); - [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(AVIMWebSocket *self, NSData *newData) { - if (isControlFrame) { - [self _handleFrameWithData:newData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO add assert that opcode is not a control; - [self _readFrameContinue]; - } - - } - } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; - } -} - -/* From RFC: - - 0 1 2 3 - 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 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ - */ - -static const uint8_t AVIMFinMask = 0x80; -static const uint8_t AVIMOpCodeMask = 0x0F; -static const uint8_t AVIMRsvMask = 0x70; -static const uint8_t AVIMMaskMask = 0x80; -static const uint8_t AVIMPayloadLenMask = 0x7F; - - -- (void)_readFrameContinue; -{ - assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); - - [self _addConsumerWithDataLength:2 callback:^(AVIMWebSocket *self, NSData *data) { - __block frame_header header = {0}; - - const uint8_t *headerBuffer = data.bytes; - assert(data.length >= 2); - - if (headerBuffer[0] & AVIMRsvMask) { - [self _closeWithProtocolError:@"Server used RSV bits"]; - return; - } - - uint8_t receivedOpcode = (AVIMOpCodeMask & headerBuffer[0]); - - BOOL isControlFrame = (receivedOpcode == AVIMOpCodePing || receivedOpcode == AVIMOpCodePong || receivedOpcode == AVIMOpCodeConnectionClose); - - if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { - [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; - return; - } - - if (receivedOpcode == 0 && self->_currentFrameCount == 0) { - [self _closeWithProtocolError:@"cannot continue a message"]; - return; - } - - header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; - - header.fin = !!(AVIMFinMask & headerBuffer[0]); - - - header.masked = !!(AVIMMaskMask & headerBuffer[1]); - header.payload_length = AVIMPayloadLenMask & headerBuffer[1]; - - headerBuffer = NULL; - - if (header.masked) { - [self _closeWithProtocolError:@"Client must receive unmasked data"]; - } - - size_t extra_bytes_needed = header.masked ? sizeof(self->_currentReadMaskKey) : 0; - - if (header.payload_length == 126) { - extra_bytes_needed += sizeof(uint16_t); - } else if (header.payload_length == 127) { - extra_bytes_needed += sizeof(uint64_t); - } - - if (extra_bytes_needed == 0) { - [self _handleFrameHeader:header curData:self->_currentFrameData]; - } else { - [self _addConsumerWithDataLength:extra_bytes_needed callback:^(AVIMWebSocket *self, NSData *data) { - size_t mapped_size = data.length; - #pragma unused (mapped_size) - const void *mapped_buffer = data.bytes; - size_t offset = 0; - - if (header.payload_length == 126) { - assert(mapped_size >= sizeof(uint16_t)); - uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); - header.payload_length = newLen; - offset += sizeof(uint16_t); - } else if (header.payload_length == 127) { - assert(mapped_size >= sizeof(uint64_t)); - header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); - offset += sizeof(uint64_t); - } else { - assert(header.payload_length < 126 && header.payload_length >= 0); - } - - - if (header.masked) { - assert(mapped_size >= sizeof(self->_currentReadMaskOffset) + offset); - memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); - } - - [self _handleFrameHeader:header curData:self->_currentFrameData]; - } readToCurrentFrame:NO unmaskBytes:NO]; - } - } readToCurrentFrame:NO unmaskBytes:NO]; -} - -- (void)_readFrameNew; -{ - dispatch_async(_workQueue, ^{ - [self->_currentFrameData setLength:0]; - - self->_currentFrameOpcode = 0; - self->_currentFrameCount = 0; - self->_readOpCount = 0; - self->_currentStringScanPosition = 0; - - [self _readFrameContinue]; - }); -} - -- (void)_pumpWriting; -{ - [self assertOnWorkQueue]; - - NSUInteger dataLength = _outputBuffer.length; - if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { - NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; - if (bytesWritten == -1) { - [self _failWithError:[NSError errorWithDomain:AVIMWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; - return; - } - - _outputBufferOffset += bytesWritten; - - if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { - _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; - _outputBufferOffset = 0; - } - } - - if (_closeWhenFinishedWriting && - _outputBuffer.length - _outputBufferOffset == 0 && - (_inputStream.streamStatus != NSStreamStatusNotOpen && - _inputStream.streamStatus != NSStreamStatusClosed) && - !_sentClose) { - _sentClose = YES; - - [_outputStream close]; - [_inputStream close]; - - - for (NSArray *runLoop in [_scheduledRunloops copy]) { - [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; - } - - if (!_failed) { - [self setReadyState:AVIMWebSocketStateClosed]; - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate && [delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES]; - } - }]; - } - - _selfRetain = nil; - } -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; -{ - [self assertOnWorkQueue]; - [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; -} - -- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -{ - [self assertOnWorkQueue]; - assert(dataLength); - - [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; - [self _pumpScanner]; -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; -{ - [self assertOnWorkQueue]; - [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; - [self _pumpScanner]; -} - - -static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; - -- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; -{ - [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; -} - -- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; -{ - // TODO optimize so this can continue from where we last searched - stream_scanner consumer = ^size_t(NSData *data) { - __block size_t found_size = 0; - __block size_t match_count = 0; - - size_t size = data.length; - const unsigned char *buffer = data.bytes; - for (size_t i = 0; i < size; i++ ) { - if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { - match_count += 1; - if (match_count == length) { - found_size = i + 1; - break; - } - } else { - match_count = 0; - } - } - return found_size; - }; - [self _addConsumerWithScanner:consumer callback:dataHandler]; -} - - -// Returns true if did work -- (BOOL)_innerPumpScanner { - - BOOL didWork = NO; - - if (self.readyState >= AVIMWebSocketStateClosing) { - return didWork; - } - - if (!_consumers.count) { - return didWork; - } - - size_t curSize = _readBuffer.length - _readBufferOffset; - if (!curSize) { - return didWork; - } - - AVIMIOConsumer *consumer = [_consumers objectAtIndex:0]; - - size_t bytesNeeded = consumer.bytesNeeded; - - size_t foundSize = 0; - if (consumer.consumer) { - NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; - foundSize = consumer.consumer(tempView); - } else { - assert(consumer.bytesNeeded); - if (curSize >= bytesNeeded) { - foundSize = bytesNeeded; - } else if (consumer.readToCurrentFrame) { - foundSize = curSize; - } - } - - NSData *slice = nil; - if (consumer.readToCurrentFrame || foundSize) { - NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); - slice = [_readBuffer subdataWithRange:sliceRange]; - - _readBufferOffset += foundSize; - - if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { - _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; - } - - if (consumer.unmaskBytes) { - NSMutableData *mutableSlice = [slice mutableCopy]; - - NSUInteger len = mutableSlice.length; - uint8_t *bytes = mutableSlice.mutableBytes; - - for (NSUInteger i = 0; i < len; i++) { - bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; - _currentReadMaskOffset += 1; - } - - slice = mutableSlice; - } - - if (consumer.readToCurrentFrame) { - [_currentFrameData appendData:slice]; - - _readOpCount += 1; - - if (_currentFrameOpcode == AVIMOpCodeTextFrame) { - // Validate UTF8 stuff. - size_t currentDataSize = _currentFrameData.length; - if (_currentFrameOpcode == AVIMOpCodeTextFrame && currentDataSize > 0) { - // TODO: Optimize the crap out of this. Don't really have to copy all the data each time - - size_t scanSize = currentDataSize - _currentStringScanPosition; - - NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; - int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); - - if (valid_utf8_size == -1) { - [self closeWithCode:AVIMStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self closeConnection]; - }); - return didWork; - } else { - _currentStringScanPosition += valid_utf8_size; - } - } - - } - - consumer.bytesNeeded -= foundSize; - - if (consumer.bytesNeeded == 0) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, nil); - [_consumerPool returnConsumer:consumer]; - didWork = YES; - } - } else if (foundSize) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, slice); - [_consumerPool returnConsumer:consumer]; - didWork = YES; - } - } - return didWork; -} - --(void)_pumpScanner; -{ - [self assertOnWorkQueue]; - - if (!_isPumping) { - _isPumping = YES; - } else { - return; - } - - while ([self _innerPumpScanner]) { - - } - - _isPumping = NO; -} - -//#define NOMASK - -static const size_t AVIMFrameHeaderOverhead = 32; - -- (void)_sendFrameWithOpcode:(AVIMOpCode)opcode data:(id)data; -{ - [self assertOnWorkQueue]; - - if (nil == data) { - return; - } - - NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData"); - - size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; - - NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + AVIMFrameHeaderOverhead]; - if (!frame) { - [self closeWithCode:AVIMStatusCodeMessageTooBig reason:@"Message too big"]; - return; - } - uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; - - // set fin - frame_buffer[0] = AVIMFinMask | opcode; - - BOOL useMask = YES; -#ifdef NOMASK - useMask = NO; -#endif - - if (useMask) { - // set the mask and header - frame_buffer[1] |= AVIMMaskMask; - } - - size_t frame_buffer_size = 2; - - const uint8_t *unmasked_payload = NULL; - if ([data isKindOfClass:[NSData class]]) { - unmasked_payload = (uint8_t *)[data bytes]; - } else if ([data isKindOfClass:[NSString class]]) { - unmasked_payload = (const uint8_t *)[data UTF8String]; - } else { - return; - } - - if (payloadLength < 126) { - frame_buffer[1] |= payloadLength; - } else if (payloadLength <= UINT16_MAX) { - frame_buffer[1] |= 126; - *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); - frame_buffer_size += sizeof(uint16_t); - } else { - frame_buffer[1] |= 127; - *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); - frame_buffer_size += sizeof(uint64_t); - } - - if (!useMask) { - for (size_t i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i]; - frame_buffer_size += 1; - } - } else { - uint8_t *mask_key = frame_buffer + frame_buffer_size; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-result" - SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); -#pragma clang diagnostic pop - - frame_buffer_size += sizeof(uint32_t); - - // TODO: could probably optimize this with SIMD - for (size_t i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; - frame_buffer_size += 1; - } - } - - assert(frame_buffer_size <= [frame length]); - frame.length = frame_buffer_size; - - [self _writeData:frame]; -} - -// MARK: - NSStreamDelegate - -- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; -{ - dispatch_async(_workQueue, ^{ - switch (eventCode) { - case NSStreamEventOpenCompleted: { - AVIMFastLog(@"NSStreamEventOpenCompleted %@", aStream); - if (self.readyState >= AVIMWebSocketStateClosing) { - return; - } - assert(self->_readBuffer); - - if (self.readyState == AVIMWebSocketStateConnecting && aStream == self->_inputStream) { - [self didConnect]; - } - [self _pumpWriting]; - [self _pumpScanner]; - break; - } - - case NSStreamEventErrorOccurred: { - AVIMFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); - /// TODO specify error better! - [self _failWithError:aStream.streamError]; - self->_readBufferOffset = 0; - [self->_readBuffer setLength:0]; - break; - - } - - case NSStreamEventEndEncountered: { - [self _pumpScanner]; - AVIMFastLog(@"NSStreamEventEndEncountered %@", aStream); - if (aStream.streamError) { - [self _failWithError:aStream.streamError]; - } else { - dispatch_async(self->_workQueue, ^{ - if (self.readyState != AVIMWebSocketStateClosed) { - [self setReadyState:AVIMWebSocketStateClosed]; - self->_selfRetain = nil; - } - - if (!self->_sentClose && !self->_failed) { - self->_sentClose = YES; - // If we get closed in this state it's probably not clean because we should be sending this when we send messages - [self _performDelegateBlock:^{ - id delegate = self.delegate; - if (delegate && [delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [delegate webSocket:self didCloseWithCode:AVIMStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; - } - }]; - } - }); - } - - break; - } - - case NSStreamEventHasBytesAvailable: { - AVIMFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); - const int bufferSize = 2048; - uint8_t buffer[bufferSize]; - - while (self->_inputStream.hasBytesAvailable) { - NSInteger bytes_read = [self->_inputStream read:buffer maxLength:bufferSize]; - - if (bytes_read > 0) { - [self->_readBuffer appendBytes:buffer length:bytes_read]; - } else if (bytes_read < 0) { - [self _failWithError:self->_inputStream.streamError]; - } - - if (bytes_read != bufferSize) { - break; - } - }; - [self _pumpScanner]; - break; - } - - case NSStreamEventHasSpaceAvailable: { - AVIMFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); - [self _pumpWriting]; - break; - } - - default: - AVIMFastLog(@"(default) %@", aStream); - break; - } - }); -} - -@end - - -@implementation AVIMIOConsumer - -@synthesize bytesNeeded = _bytesNeeded; -@synthesize consumer = _scanner; -@synthesize handler = _handler; -@synthesize readToCurrentFrame = _readToCurrentFrame; -@synthesize unmaskBytes = _unmaskBytes; - -- (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -{ - _scanner = [scanner copy]; - _handler = [handler copy]; - _bytesNeeded = bytesNeeded; - _readToCurrentFrame = readToCurrentFrame; - _unmaskBytes = unmaskBytes; - assert(_scanner || _bytesNeeded); -} - - -@end - - -@implementation AVIMIOConsumerPool { - NSUInteger _poolSize; - NSMutableArray *_bufferedConsumers; -} - -- (id)initWithBufferCapacity:(NSUInteger)poolSize; -{ - self = [super init]; - if (self) { - _poolSize = poolSize; - _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; - } - return self; -} - -- (id)init -{ - return [self initWithBufferCapacity:8]; -} - -- (AVIMIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -{ - AVIMIOConsumer *consumer = nil; - if (_bufferedConsumers.count) { - consumer = [_bufferedConsumers lastObject]; - [_bufferedConsumers removeLastObject]; - } else { - consumer = [[AVIMIOConsumer alloc] init]; - } - - [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; - - return consumer; -} - -- (void)returnConsumer:(AVIMIOConsumer *)consumer; -{ - if (_bufferedConsumers.count < _poolSize) { - [_bufferedConsumers addObject:consumer]; - } -} - -@end - -@implementation NSURL (AVIMWebSocket) - -- (NSString *)AVIM_origin; -{ - NSString *scheme = [self.scheme lowercaseString]; - - if ([scheme isEqualToString:@"wss"]) { - scheme = @"https"; - } else if ([scheme isEqualToString:@"ws"]) { - scheme = @"http"; - } - - BOOL portIsDefault = !self.port || - ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) || - ([scheme isEqualToString:@"https"] && self.port.integerValue == 443); - - if (!portIsDefault) { - return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port]; - } else { - return [NSString stringWithFormat:@"%@://%@", scheme, self.host]; - } -} - -@end - -//#define AVIM_ENABLE_LOG - -static inline void AVIMFastLog(NSString *format, ...) { -#ifdef AVIM_ENABLE_LOG - __block va_list arg_list; - va_start (arg_list, format); - - NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; - - va_end(arg_list); - - NSLog(@"[AVIM] %@", formattedString); -#endif -} - - -#ifdef HAS_ICU - -static inline int32_t validate_dispatch_data_partial_string(NSData *data) { - if ([data length] > INT32_MAX) { - // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. - return -1; - } - - int32_t size = (int32_t)[data length]; - - const void * contents = [data bytes]; - const uint8_t *str = (const uint8_t *)contents; - - UChar32 codepoint = 1; - int32_t offset = 0; - int32_t lastOffset = 0; - while(offset < size && codepoint > 0) { - lastOffset = offset; - U8_NEXT(str, offset, size, codepoint); - } - - if (codepoint == -1) { - // Check to see if the last byte is valid or whether it was just continuing - if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { - - size = -1; - } else { - uint8_t leadByte = str[lastOffset]; - U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); - - for (int i = lastOffset + 1; i < offset; i++) { - if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { - size = -1; - } - } - - if (size != -1) { - size = lastOffset; - } - } - } - - if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { - size = -1; - } - - return size; -} - -#else - -// This is a hack, and probably not optimal -static inline int32_t validate_dispatch_data_partial_string(NSData *data) { - static const int maxCodepointSize = 3; - - for (int i = 0; i < maxCodepointSize; i++) { - NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; - if (str) { - return (int32_t)data.length - i; - } - } - - return -1; -} - -#endif - -static _AVIMRunLoopThread *networkThread = nil; -static NSRunLoop *networkRunLoop = nil; - -@implementation NSRunLoop (AVIMWebSocket) - -+ (NSRunLoop *)AVIM_networkRunLoop { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - networkThread = [[_AVIMRunLoopThread alloc] init]; - networkThread.name = @"cn.leancloud.im.websocket"; - [networkThread start]; - networkRunLoop = networkThread.runLoop; - }); - - return networkRunLoop; -} - -@end - - -@implementation _AVIMRunLoopThread { - dispatch_group_t _waitGroup; -} - -@synthesize runLoop = _runLoop; - -- (void)dealloc -{ - avim_dispatch_release(_waitGroup); -} - -- (id)init -{ - self = [super init]; - if (self) { - _waitGroup = dispatch_group_create(); - dispatch_group_enter(_waitGroup); - } - return self; -} - -- (void)main; -{ - @autoreleasepool { - _runLoop = [NSRunLoop currentRunLoop]; - dispatch_group_leave(_waitGroup); - - // Add an empty run loop source to prevent runloop from spinning. - CFRunLoopSourceContext sourceCtx = { - .version = 0, - .info = NULL, - .retain = NULL, - .release = NULL, - .copyDescription = NULL, - .equal = NULL, - .hash = NULL, - .schedule = NULL, - .cancel = NULL, - .perform = NULL - }; - CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); - CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); - CFRelease(source); - - while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { - - } - assert(NO); - } -} - -- (NSRunLoop *)runLoop; -{ - dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); - return _runLoop; -} - -@end diff --git a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m index 27e3079ba..652fe602b 100644 --- a/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m +++ b/AVOS/AVOSCloudIM/WebSocket/AVIMWebSocketWrapper.m @@ -146,6 +146,7 @@ @interface AVIMWebSocketWrapper () @property (atomic, assign) BOOL isApplicationInBackground; @property (atomic, assign) LCNetworkReachabilityStatus currentNetworkReachabilityStatus; +@property (nonatomic) BOOL isWebSocketOpened; @end @@ -275,6 +276,7 @@ - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; [self->_reachabilityMonitor stopMonitoring]; + [self->_websocket safeClean]; } // MARK: - Application Notification @@ -346,7 +348,7 @@ - (void)openWithCallback:(void (^)(BOOL succeeded, NSError *error))callback { [self addOperationToInternalSerialQueue:^(AVIMWebSocketWrapper *websocketWrapper) { LCRTMWebSocket *websocket = websocketWrapper->_websocket; - if (websocket && websocket.isConnected) { + if (websocket && websocketWrapper.isWebSocketOpened) { callback(true, nil); } else { [websocketWrapper purgeWithError:({ @@ -383,10 +385,12 @@ - (void)close // MARK: - Websocket Open & Close Notification -- (void)websocketDidConnect:(LCRTMWebSocket *)socket { +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didOpenWithProtocol:(NSString *)protocol +{ AssertRunInQueue(self->_internalSerialQueue); NSParameterAssert(socket == self->_websocket); - AVLoggerInfo(AVLoggerDomainIM, @" websocket did open.", socket); + AVLoggerInfo(AVLoggerDomainIM, @" websocket did open with protocol: %@.", socket, protocol ?: @"nil"); + self.isWebSocketOpened = true; if (self->_openTimeoutBlock) { dispatch_block_cancel(self->_openTimeoutBlock); self->_openTimeoutBlock = nil; @@ -404,7 +408,8 @@ - (void)websocketDidConnect:(LCRTMWebSocket *)socket { } } -- (void)websocketDidDisconnect:(LCRTMWebSocket *)socket error:(NSError *)error { +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didCloseWithError:(NSError *)error +{ AssertRunInQueue(self->_internalSerialQueue); NSParameterAssert(socket == self->_websocket); if (!error) { @@ -428,7 +433,7 @@ - (void)sendCommandWrapper:(LCIMProtobufCommandWrapper *)commandWrapper } LCRTMWebSocket *webSocket = websocketWrapper->_websocket; id delegate = websocketWrapper->_delegate; - if (!webSocket || !webSocket.isConnected) { + if (!webSocket || !websocketWrapper.isWebSocketOpened) { commandWrapper.error = ({ AVIMErrorCode code = AVIMErrorCodeConnectionLost; LCError(code, AVIMErrorMessage(code), nil); @@ -456,47 +461,50 @@ - (void)sendCommandWrapper:(LCIMProtobufCommandWrapper *)commandWrapper websocketWrapper->_commandWrapperMap[index] = commandWrapper; [websocketWrapper->_serialIndexArray addObject:index]; } - AVLoggerInfo(AVLoggerDomainIM, LCIM_OUT_COMMAND_LOG_FORMAT, [commandWrapper.outCommand avim_description]); - [webSocket writeData:data]; + [webSocket sendMessage:[LCRTMWebSocketMessage messageWithData:data] completion:^{ + AVLoggerInfo(AVLoggerDomainIM, LCIM_OUT_COMMAND_LOG_FORMAT, [commandWrapper.outCommand avim_description]); + }]; }]; } -- (void)websocket:(LCRTMWebSocket *)socket didReceiveData:(NSData *)data { - AssertRunInQueue(self->_internalSerialQueue); - AVIMGenericCommand *inCommand = ({ - NSError *error = nil; - AVIMGenericCommand *inCommand = [AVIMGenericCommand parseFromData:data error:&error]; - if (!inCommand) { - AVLoggerError(AVLoggerDomainIM, @"did receive message with error: %@", error); - return; - } - inCommand; - }); - AVLoggerInfo(AVLoggerDomainIM, LCIM_IN_COMMAND_LOG_FORMAT, [inCommand avim_description]); - id delegate = self->_delegate; - if (inCommand.hasI && inCommand.i > 0) { - NSNumber *index = @(inCommand.i); - LCIMProtobufCommandWrapper *commandWrapper = self->_commandWrapperMap[index]; - if (commandWrapper) { - [self->_commandWrapperMap removeObjectForKey:index]; - [self->_serialIndexArray removeObject:index]; +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceiveMessage:(LCRTMWebSocketMessage *)message +{ + if (message.type == LCRTMWebSocketMessageTypeData) { + NSData *data = message.data; + AssertRunInQueue(self->_internalSerialQueue); + AVIMGenericCommand *inCommand = ({ + NSError *error = nil; + AVIMGenericCommand *inCommand = [AVIMGenericCommand parseFromData:data error:&error]; + if (!inCommand) { + AVLoggerError(AVLoggerDomainIM, @"did receive message with error: %@", error); + return; + } + inCommand; + }); + AVLoggerInfo(AVLoggerDomainIM, LCIM_IN_COMMAND_LOG_FORMAT, [inCommand avim_description]); + id delegate = self->_delegate; + if (inCommand.hasI && inCommand.i > 0) { + NSNumber *index = @(inCommand.i); + LCIMProtobufCommandWrapper *commandWrapper = self->_commandWrapperMap[index]; + if (commandWrapper) { + [self->_commandWrapperMap removeObjectForKey:index]; + [self->_serialIndexArray removeObject:index]; + commandWrapper.inCommand = inCommand; + [delegate webSocketWrapper:self didReceiveCommandCallback:commandWrapper]; + } + } else { + LCIMProtobufCommandWrapper *commandWrapper = [LCIMProtobufCommandWrapper new]; commandWrapper.inCommand = inCommand; - [delegate webSocketWrapper:self didReceiveCommandCallback:commandWrapper]; + [delegate webSocketWrapper:self didReceiveCommand:commandWrapper]; + if (!inCommand.hasPeerId) { + [self handleGoawayWith:inCommand]; + } } } else { - LCIMProtobufCommandWrapper *commandWrapper = [LCIMProtobufCommandWrapper new]; - commandWrapper.inCommand = inCommand; - [delegate webSocketWrapper:self didReceiveCommand:commandWrapper]; - if (!inCommand.hasPeerId) { - [self handleGoawayWith:inCommand]; - } + // NOP } } -- (void)websocket:(LCRTMWebSocket *)socket didReceiveMessage:(NSString *)string { - /// NOP -} - // MARK: - Ping Sender - (void)startPingSender @@ -528,20 +536,33 @@ - (void)sendPing { AssertRunInQueue(self->_internalSerialQueue); LCRTMWebSocket *websocket = self->_websocket; - if (!websocket || !websocket.isConnected) { + if (!websocket || !self.isWebSocketOpened) { return; } - AVLoggerInfo(AVLoggerDomainIM, @" websocket send ping.", websocket); self->_lastPingTimestamp = NSDate.date.timeIntervalSince1970; - [websocket writePing:[NSData data]]; + NSString *address = [NSString stringWithFormat:@"%p", websocket]; + [websocket sendPing:[NSData data] completion:^{ + AVLoggerInfo(AVLoggerDomainIM, @" websocket send ping.", address); + }]; } -- (void)websocket:(LCRTMWebSocket *)socket didReceivePong:(NSData *)data { +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceivePong:(NSData *)data +{ AssertRunInQueue(self->_internalSerialQueue); AVLoggerInfo(AVLoggerDomainIM, @" websocket did receive pong.", socket); self->_didReceivePong = true; } +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceivePing:(NSData *)data +{ + AssertRunInQueue(self->_internalSerialQueue); + AVLoggerInfo(AVLoggerDomainIM, @" websocket did receive ping.", socket); + NSString *address = [NSString stringWithFormat:@"%p", socket]; + [socket sendPong:data completion:^{ + AVLoggerInfo(AVLoggerDomainIM, @" websocket send pong.", address); + }]; +} + // MARK: - Timeout Checker - (void)startTimeoutChecker @@ -710,9 +731,10 @@ - (void)tryConnecting:(BOOL)force NSArray *protocols = RTMProtocols(); NSURL *URL = [NSURL URLWithString:server]; self->_websocket = [[LCRTMWebSocket alloc] initWithURL:URL protocols:protocols]; - self->_websocket.queue = self->_internalSerialQueue; + self->_websocket.delegateQueue = self->_internalSerialQueue; self->_websocket.delegate = self; - [self->_websocket connect]; + [self->_websocket.request setValue:nil forHTTPHeaderField:@"Origin"]; + [self->_websocket open]; __weak typeof(self) weakSelf = self; self->_openTimeoutBlock = dispatch_block_create(0, ^{ self->_openTimeoutBlock = nil; @@ -761,11 +783,14 @@ - (void)getRTMServerWithCallback:(void (^)(NSString *server, NSError *error))cal - (void)purgeWithError:(NSError *)error { AssertRunInQueue(self->_internalSerialQueue); + self.isWebSocketOpened = false; // discard websocket if (self->_websocket) { AVLoggerInfo(AVLoggerDomainIM, @" websocket discard.", self->_websocket); self->_websocket.delegate = nil; - [self->_websocket disconnect]; + [self->_websocket closeWithCloseCode:LCRTMWebSocketCloseCodeNormalClosure + reason:nil]; + [self->_websocket safeClean]; self->_websocket = nil; } if (self->_openTimeoutBlock) { diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h deleted file mode 100644 index 07278f1c3..000000000 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.h +++ /dev/null @@ -1,82 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// LCRTMSecurity.h -// -// Created by Austin and Dalton Cherry on on 9/3/15. -// Copyright (c) 2014-2017 Austin Cherry. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -#import -#import - -@interface LCRTMSSLCert : NSObject - -/** - Designated init for certificates - - :param: data is the binary data of the certificate - - :returns: a representation security object to be used with - */ -- (instancetype)initWithData:(NSData *)data; - - -/** - Designated init for public keys - - :param: key is the public key to be used - - :returns: a representation security object to be used with - */ -- (instancetype)initWithKey:(SecKeyRef)key; - -@end - -@interface LCRTMSecurity : NSObject - -/** - Use certs from main app bundle - - :param usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation - - :returns: a representation security object to be used with - */ -- (instancetype)initWithCerts:(NSArray*)certs publicKeys:(BOOL)publicKeys; - -/** - Designated init - - :param keys: is the certificates or public keys to use - :param usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation - - :returns: a representation security object to be used with - */ -- (instancetype)initUsingPublicKeys:(BOOL)publicKeys; - -/** - Should the domain name be validated? Default is YES. - */ -@property(nonatomic)BOOL validatedDN; - -/** - Validate if the cert is legit or not. - :param: trust is the trust to validate - :param: domain to validate along with the trust (can be nil) - :return: YES or NO if valid. - */ -- (BOOL)isValid:(SecTrustRef)trust domain:(NSString*)domain; - -@end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m deleted file mode 100644 index 98dcaa606..000000000 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMSecurity.m +++ /dev/null @@ -1,218 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// LCRTMSecurity.m -// -// Created by Austin and Dalton Cherry on on 9/3/15. -// Copyright (c) 2014-2017 Austin Cherry. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -#import "LCRTMSecurity.h" - -@interface LCRTMSSLCert () - -@property(nonatomic, strong)NSData *certData; -@property(nonatomic)SecKeyRef key; - -@end - -@implementation LCRTMSSLCert - -///////////////////////////////////////////////////////////////////////////// -- (instancetype)initWithData:(NSData *)data { - if(self = [super init]) { - self.certData = data; - } - return self; -} -//////////////////////////////////////////////////////////////////////////// -- (instancetype)initWithKey:(SecKeyRef)key { - if(self = [super init]) { - self.key = key; - } - return self; -} -//////////////////////////////////////////////////////////////////////////// -- (void)dealloc { - if(self.key) { - CFRelease(self.key); - } -} -//////////////////////////////////////////////////////////////////////////// - -@end - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -@interface LCRTMSecurity () - -@property(nonatomic)BOOL isReady; //is the key processing done? -@property(nonatomic, strong)NSMutableArray *certificates; -@property(nonatomic, strong)NSMutableArray *pubKeys; -@property(nonatomic)BOOL usePublicKeys; - -@end - -@implementation LCRTMSecurity - -///////////////////////////////////////////////////////////////////////////// -- (instancetype)initUsingPublicKeys:(BOOL)publicKeys { - NSArray *paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"cer" inDirectory:@"."]; - NSMutableArray *collect = [NSMutableArray array]; - for(NSString *path in paths) { - NSData *data = [NSData dataWithContentsOfFile:path]; - if(data) { - [collect addObject:[[LCRTMSSLCert alloc] initWithData:data]]; - } - } - return [self initWithCerts:collect publicKeys:publicKeys]; -} -///////////////////////////////////////////////////////////////////////////// -- (instancetype)initWithCerts:(NSArray*)certs publicKeys:(BOOL)publicKeys { - if(self = [super init]) { - self.validatedDN = YES; - self.usePublicKeys = publicKeys; - if(self.usePublicKeys) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ - NSMutableArray *collect = [NSMutableArray array]; - for(LCRTMSSLCert *cert in certs) { - if(cert.certData && !cert.key) { - cert.key = [self extractPublicKey:cert.certData]; - } - if(cert.key) { - [collect addObject:CFBridgingRelease(cert.key)]; - } - } - self.certificates = collect; - self.isReady = YES; - }); - } else { - NSMutableArray *collect = [NSMutableArray array]; - for(LCRTMSSLCert *cert in certs) { - if(cert.certData) { - [collect addObject:cert.certData]; - } - } - self.certificates = collect; - self.isReady = YES; - } - } - return self; -} -///////////////////////////////////////////////////////////////////////////// -- (BOOL)isValid:(SecTrustRef)trust domain:(NSString*)domain { - int tries = 0; - while (!self.isReady) { - usleep(1000); - tries++; - if(tries > 5) { - return NO; //doesn't appear it is going to ever be ready... - } - } - BOOL status = NO; - SecPolicyRef policy; - if(self.validatedDN) { - policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)domain); - } else { - policy = SecPolicyCreateBasicX509(); - } - SecTrustSetPolicies(trust,policy); - if(self.usePublicKeys) { - for(id serverKey in [self publicKeyChainForTrust:trust]) { - for(id keyObj in self.pubKeys) { - if([serverKey isEqual:keyObj]) { - status = YES; - break; - } - } - } - } else { - NSArray *serverCerts = [self certificateChainForTrust:trust]; - NSMutableArray *collect = [NSMutableArray arrayWithCapacity:self.certificates.count]; - for(NSData *data in self.certificates) { - [collect addObject:CFBridgingRelease(SecCertificateCreateWithData(nil,(__bridge CFDataRef)data))]; - } - SecTrustSetAnchorCertificates(trust,(__bridge CFArrayRef)collect); - SecTrustResultType result = 0; - SecTrustEvaluate(trust,&result); - if(result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) { - NSInteger trustedCount = 0; - for(NSData *serverData in serverCerts) { - for(NSData *certData in self.certificates) { - if([certData isEqualToData:serverData]) { - trustedCount++; - break; - } - } - } - if(trustedCount == serverCerts.count) { - status = YES; - } - } - } - - CFRelease(policy); - return status; -} -///////////////////////////////////////////////////////////////////////////// -- (SecKeyRef)extractPublicKey:(NSData*)data { - SecCertificateRef possibleKey = SecCertificateCreateWithData(nil,(__bridge CFDataRef)data); - SecPolicyRef policy = SecPolicyCreateBasicX509(); - SecKeyRef key = [self extractPublicKeyFromCert:possibleKey policy:policy]; - CFRelease(policy); - CFRelease(possibleKey); - return key; -} -///////////////////////////////////////////////////////////////////////////// -- (SecKeyRef)extractPublicKeyFromCert:(SecCertificateRef)cert policy:(SecPolicyRef)policy { - - SecTrustRef trust; - SecTrustCreateWithCertificates(cert,policy,&trust); - SecTrustResultType result = kSecTrustResultInvalid; - SecTrustEvaluate(trust,&result); - SecKeyRef key = SecTrustCopyPublicKey(trust); - CFRelease(trust); - return key; -} -///////////////////////////////////////////////////////////////////////////// -- (NSArray*)certificateChainForTrust:(SecTrustRef)trust { - NSMutableArray *collect = [NSMutableArray array]; - for(int i = 0; i < SecTrustGetCertificateCount(trust); i++) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust,i); - if(cert) { - [collect addObject:CFBridgingRelease(SecCertificateCopyData(cert))]; - } - } - return collect; -} -///////////////////////////////////////////////////////////////////////////// -- (NSArray*)publicKeyChainForTrust:(SecTrustRef)trust { - NSMutableArray *collect = [NSMutableArray array]; - SecPolicyRef policy = SecPolicyCreateBasicX509(); - for(int i = 0; i < SecTrustGetCertificateCount(trust); i++) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust,i); - SecKeyRef key = [self extractPublicKeyFromCert:cert policy:policy]; - if(key) { - [collect addObject:CFBridgingRelease(key)]; - } - } - CFRelease(policy); - return collect; -} -///////////////////////////////////////////////////////////////////////////// - -@end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h index 067ca6add..0a0bf892a 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.h @@ -1,144 +1,85 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// // // LCRTMWebSocket.h +// AVOS // -// Created by Austin and Dalton Cherry on on 5/13/14. -// Copyright (c) 2014-2017 Austin Cherry. +// Created by zapcannon87 on 2020/4/23. +// Copyright © 2020 LeanCloud Inc. All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// #import -#import "LCRTMSecurity.h" + +typedef NS_ENUM(NSInteger, LCRTMWebSocketCloseCode) +{ + LCRTMWebSocketCloseCodeInvalid = 0, + LCRTMWebSocketCloseCodeNormalClosure = 1000, + LCRTMWebSocketCloseCodeGoingAway = 1001, + LCRTMWebSocketCloseCodeProtocolError = 1002, + LCRTMWebSocketCloseCodeUnsupportedData = 1003, + LCRTMWebSocketCloseCodeNoStatusReceived = 1005, + LCRTMWebSocketCloseCodeAbnormalClosure = 1006, + LCRTMWebSocketCloseCodeInvalidFramePayloadData = 1007, + LCRTMWebSocketCloseCodePolicyViolation = 1008, + LCRTMWebSocketCloseCodeMessageTooBig = 1009, + LCRTMWebSocketCloseCodeMandatoryExtensionMissing = 1010, + LCRTMWebSocketCloseCodeInternalServerError = 1011, + LCRTMWebSocketCloseCodeTLSHandshakeFailure = 1015, +}; + +typedef NS_ENUM(NSInteger, LCRTMWebSocketMessageType) { + LCRTMWebSocketMessageTypeData = 0, + LCRTMWebSocketMessageTypeString = 1, +}; + +@interface LCRTMWebSocketMessage : NSObject + ++ (instancetype)messageWithData:(NSData *)data; ++ (instancetype)messageWithString:(NSString *)string; + +- (instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithString:(NSString *)string NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly) LCRTMWebSocketMessageType type; +@property (nonatomic, readonly) NSData *data; +@property (nonatomic, readonly) NSString *string; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end @class LCRTMWebSocket; -/** - It is important to note that all the delegate methods are put back on the main thread. - This means if you want to do some major process of the data, you need to create a background thread. - */ @protocol LCRTMWebSocketDelegate -/** - The websocket connected to its host. - @param socket is the current socket object. - */ --(void)websocketDidConnect:(nonnull LCRTMWebSocket*)socket; - -/** - The websocket was disconnected from its host. - @param socket is the current socket object. - @param error is return an error occured to trigger the disconnect. - */ --(void)websocketDidDisconnect:(nonnull LCRTMWebSocket*)socket error:(nullable NSError*)error; - -/** - The websocket got a text based message. - @param socket is the current socket object. - @param string is the text based data that has been returned. - */ --(void)websocket:(nonnull LCRTMWebSocket*)socket didReceiveMessage:(nonnull NSString*)string; - -/** - The websocket got a binary based message. - @param socket is the current socket object. - @param data is the binary based data that has been returned. - */ --(void)websocket:(nonnull LCRTMWebSocket*)socket didReceiveData:(nullable NSData*)data; - -/** - The websocket got a pong. - @param socket is the current socket object. - */ --(void)websocket:(nonnull LCRTMWebSocket*)socket didReceivePong:(nullable NSData*)data; +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didOpenWithProtocol:(NSString *)protocol; + +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didCloseWithError:(NSError *)error; + +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceiveMessage:(LCRTMWebSocketMessage *)message; + +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceivePing:(NSData *)data; + +- (void)LCRTMWebSocket:(LCRTMWebSocket *)socket didReceivePong:(NSData *)data; @end @interface LCRTMWebSocket : NSObject -@property(nonatomic, weak, nullable)id delegate; -@property(nonatomic, strong, readonly, nonnull)NSURL *url; - -/** - constructor to create a new websocket. - @param url the host you want to connect to. - @param protocols the websocket protocols you want to use (e.g. chat,superchat). - @return a newly initalized websocket. - */ -- (nonnull instancetype)initWithURL:(nonnull NSURL *)url protocols:(nullable NSArray*)protocols; - -/** - connect to the host. - */ -- (void)connect; - -/** - disconnect to the host. This sends the close Connection opcode to terminate cleanly. - */ -- (void)disconnect; - -/** - write binary based data to the socket. - @param data the binary data to write. - */ -- (void)writeData:(nonnull NSData*)data; - -/** - write text based data to the socket. - @param string the string to write. - */ -- (void)writeString:(nonnull NSString*)string; - -/** - write ping to the socket. - @param data the binary data to write (if desired). - */ -- (void)writePing:(nonnull NSData*)data; - -/** - Add a header to send along on the the HTTP connect. - @param value the string to send - @param key the HTTP key name to send - */ -- (void)addHeader:(nonnull NSString*)value forKey:(nonnull NSString*)key; - -/** - returns if the socket is conneted or not. - */ -@property(atomic, assign, readonly)BOOL isConnected; - -/** - Enable VOIP support on the socket, so it can be used in the background for VOIP calls. - Default setting is No. - */ -@property(nonatomic, assign)BOOL voipEnabled; - -/** - Allows connection to self signed or untrusted WebSocket connection. Useful for development. - Default setting is No. - */ -@property(nonatomic, assign)BOOL selfSignedSSL; - -/** - Use for SSL pinning. - */ -@property(nonatomic, strong, nullable)LCRTMSecurity *security; - -/** - Set your own custom queue. - Default setting is dispatch_get_main_queue. - */ -@property(nonatomic, strong, nonnull)dispatch_queue_t queue; +@property (nonatomic, weak) id delegate; +@property (nonatomic) dispatch_queue_t delegateQueue; +@property (nonatomic) NSMutableURLRequest *request; + +- (instancetype)initWithURL:(NSURL *)url; +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +- (instancetype)initWithRequest:(NSURLRequest *)request; + +- (void)open; +- (void)closeWithCloseCode:(LCRTMWebSocketCloseCode)closeCode reason:(NSData *)reason; + +- (void)sendMessage:(LCRTMWebSocketMessage *)message completion:(void (^)(void))completion; +- (void)sendPing:(NSData *)data completion:(void (^)(void))completion; +- (void)sendPong:(NSData *)data completion:(void (^)(void))completion; + +- (void)safeClean; @end diff --git a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m index 94a1cd92b..0bf1b25d0 100644 --- a/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m +++ b/AVOS/AVOSCloudIM/WebSocket/LCRTMWebSocket.m @@ -1,992 +1,1002 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// // // LCRTMWebSocket.m +// AVOS // -// Created by Austin and Dalton Cherry on on 5/13/14. -// Copyright (c) 2014-2017 Austin Cherry. +// Created by zapcannon87 on 2020/4/23. +// Copyright © 2020 LeanCloud Inc. All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// #import "LCRTMWebSocket.h" -#import "AVErrorUtils.h" - -//get the opCode from the packet -typedef NS_ENUM(NSUInteger, LCRTMOpCode) { - LCRTMOpCodeContinueFrame = 0x0, - LCRTMOpCodeTextFrame = 0x1, - LCRTMOpCodeBinaryFrame = 0x2, - //3-7 are reserved. - LCRTMOpCodeConnectionClose = 0x8, - LCRTMOpCodePing = 0x9, - LCRTMOpCodePong = 0xA, - //B-F reserved. -}; +#import +#import -typedef NS_ENUM(NSUInteger, LCRTMCloseCode) { - LCRTMCloseCodeNormal = 1000, - LCRTMCloseCodeGoingAway = 1001, - LCRTMCloseCodeProtocolError = 1002, - LCRTMCloseCodeProtocolUnhandledType = 1003, - // 1004 reserved. - LCRTMCloseCodeNoStatusReceived = 1005, - // 1006 reserved. - LCRTMCloseCodeEncoding = 1007, - LCRTMCloseCodePolicyViolated = 1008, - LCRTMCloseCodeMessageTooBig = 1009 -}; +@interface LCRTMWebSocketConnectionClosure : NSObject -typedef NS_ENUM(NSUInteger, LCRTMInternalErrorCode) { - // 0-999 WebSocket status codes not used - LCRTMOutputStreamWriteError = 1, //Output stream error during write - LCRTMInvalidSSLError = 2, //Invalid SSL certificate - LCRTMWriteTimeoutError = 3, //The socket timed out waiting to be ready to write - LCRTMUpgradeError = 4, //There was an error during the HTTP upgrade - LCRTMCloseError = 5 //There was an error during the close (socket probably has been dereferenced) -}; +@property (nonatomic) LCRTMWebSocketCloseCode closeCode; +@property (nonatomic) NSString *reason; +@property (nonatomic) NSDictionary *userInfo; -#define kLCRTMInternalHTTPStatusWebSocket 101 +@end -//holds the responses in our read stack to properly process messages -@interface LCRTMResponse : NSObject +@implementation LCRTMWebSocketConnectionClosure -@property(nonatomic, assign)BOOL isFin; -@property(nonatomic, assign)LCRTMOpCode code; -@property(nonatomic, assign)NSInteger bytesLeft; -@property(nonatomic, assign)NSInteger frameCount; -@property(nonatomic, strong)NSMutableData *buffer; +- (NSError *)error +{ + NSDictionary *userInfo = self.userInfo; + if (self.reason) { + NSMutableDictionary *dic = [userInfo mutableCopy] ?: [NSMutableDictionary dictionary]; + dic[NSLocalizedFailureReasonErrorKey] = self.reason; + userInfo = dic; + } + return [NSError errorWithDomain:@"LCRTMWebSocketErrorDomain" + code:self.closeCode + userInfo:userInfo]; +} @end -@interface LCRTMWebSocket () - -@property(nonatomic, strong, nonnull)NSURL *url; -@property(nonatomic, strong, nonnull)NSOperationQueue *writeQueue; -@property(nonatomic, strong, nonnull)dispatch_queue_t streamQueue; -@property(nonatomic, strong)NSInputStream *inputStream; -@property(nonatomic, strong)NSOutputStream *outputStream; -@property(nonatomic, assign)BOOL isRunLoop; -@property(nonatomic, strong, nonnull)NSMutableArray *readStack; -@property(nonatomic, strong, nonnull)NSMutableArray *inputQueue; -@property(nonatomic, strong, nullable)NSData *fragBuffer; -@property(nonatomic, strong, nullable)NSMutableDictionary *headers; -@property(nonatomic, strong, nullable)NSArray *optProtocols; -@property(atomic, assign)BOOL isConnected; -@property(nonatomic, assign)BOOL isCreated; -@property(nonatomic, assign)BOOL didDisconnect; -@property(nonatomic, assign)BOOL certValidated; +@implementation LCRTMWebSocketMessage -@end ++ (instancetype)messageWithData:(NSData *)data +{ + return [[self alloc] initWithData:data]; +} -//Constant Header Values. -NS_ASSUME_NONNULL_BEGIN -static NSString *const headerWSUpgradeName = @"Upgrade"; -static NSString *const headerWSUpgradeValue = @"websocket"; -static NSString *const headerWSHostName = @"Host"; -static NSString *const headerWSConnectionName = @"Connection"; -static NSString *const headerWSConnectionValue = @"Upgrade"; -static NSString *const headerWSProtocolName = @"Sec-WebSocket-Protocol"; -static NSString *const headerWSVersionName = @"Sec-Websocket-Version"; -static NSString *const headerWSVersionValue = @"13"; -static NSString *const headerWSKeyName = @"Sec-WebSocket-Key"; -static NSString *const headerOriginName = @"Origin"; -static NSString *const headerWSAcceptName = @"Sec-WebSocket-Accept"; -NS_ASSUME_NONNULL_END - -//Class Constants -static char CRLFBytes[] = {'\r', '\n', '\r', '\n'}; -static int BUFFER_MAX = 4096; - -// This get the correct bits out by masking the bytes of the buffer. -static const uint8_t LCRTMFinMask = 0x80; -static const uint8_t LCRTMOpCodeMask = 0x0F; -static const uint8_t LCRTMRSVMask = 0x70; -static const uint8_t LCRTMMaskMask = 0x80; -static const uint8_t LCRTMPayloadLenMask = 0x7F; -static const size_t LCRTMMaxFrameSize = 32; ++ (instancetype)messageWithString:(NSString *)string +{ + return [[self alloc] initWithString:string]; +} -@implementation LCRTMWebSocket +- (instancetype)initWithData:(NSData *)data +{ + self = [super init]; + if (self) { + _type = LCRTMWebSocketMessageTypeData; + _data = data; + } + return self; +} -///////////////////////////////////////////////////////////////////////////// -//Default initializer -- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray*)protocols +- (instancetype)initWithString:(NSString *)string { self = [super init]; if (self) { - self.certValidated = NO; - self.voipEnabled = NO; - self.selfSignedSSL = NO; - self.queue = dispatch_get_main_queue(); - self.streamQueue = dispatch_queue_create("LCRTMWebSocket.streamQueue", DISPATCH_QUEUE_SERIAL); - self.url = url; - self.readStack = [NSMutableArray new]; - self.inputQueue = [NSMutableArray new]; - self.optProtocols = protocols; + _type = LCRTMWebSocketMessageTypeString; + _string = string; } return self; } -///////////////////////////////////////////////////////////////////////////// -//Exposed method for connecting to URL provided in init method. -- (void)connect { - if(self.isCreated) { - return; + +@end + +typedef NS_ENUM(UInt8, LCRTMWebSocketOpcode) { + LCRTMWebSocketOpcodeContinuation = 0x0, + LCRTMWebSocketOpcodeText = 0x1, + LCRTMWebSocketOpcodeBinary = 0x2, + LCRTMWebSocketOpcodeConnectionClose = 0x8, + LCRTMWebSocketOpcodePing = 0x9, + LCRTMWebSocketOpcodePong = 0xA, +}; + +@interface LCRTMWebSocketFrame : NSObject + +@property (nonatomic) BOOL isFIN; +@property (nonatomic) LCRTMWebSocketOpcode opcode; +@property (nonatomic) NSUInteger totalSize; +@property (nonatomic) NSData *payload; + +@property (nonatomic) NSUInteger offset; +@property (nonatomic) void(^completion)(void); + +@end + +@implementation LCRTMWebSocketFrame + +static const UInt8 LCRTMWebSocketFrameBitMaskFIN = 0x80; +static const UInt8 LCRTMWebSocketFrameBitMaskRSV = 0x70; +static const UInt8 LCRTMWebSocketFrameBitMaskOpcode = 0x0F; +static const UInt8 LCRTMWebSocketFrameBitMaskMask = 0x80; +static const UInt8 LCRTMWebSocketFrameBitMaskPayloadLength = 0x7F; + ++ (LCRTMWebSocketFrame *)frameFrom:(UInt8 *)buffer + length:(NSUInteger)bufferLength + connectionClosure:(LCRTMWebSocketConnectionClosure * __autoreleasing *)ccPtr +{ + // FIN + BOOL isFIN = (buffer[0] & LCRTMWebSocketFrameBitMaskFIN); + // RSV + if ((buffer[0] & LCRTMWebSocketFrameBitMaskRSV) != 0) { + *ccPtr = [self protocolError:@"a nonzero RSV value is received"]; + return nil; + } + // Opcode + UInt8 opcode = (buffer[0] & LCRTMWebSocketFrameBitMaskOpcode); + BOOL isControlFrame = (opcode >= LCRTMWebSocketOpcodeConnectionClose + && opcode <= LCRTMWebSocketOpcodePong); + if (opcode > LCRTMWebSocketOpcodeBinary && !isControlFrame) { + *ccPtr = [self protocolError:@"an unknown opcode is received"]; + return nil; + } else if (isControlFrame && !isFIN) { + *ccPtr = [self protocolError:@"all control frames must not be fragmented"]; + return nil; + } + // Mask + if ((buffer[1] & LCRTMWebSocketFrameBitMaskMask) != 0) { + *ccPtr = [self protocolError:@"detects a masked frame from server"]; + return nil; + } + // Payload length + NSUInteger payloadLength = (NSUInteger)(buffer[1] & LCRTMWebSocketFrameBitMaskPayloadLength); + if (isControlFrame && payloadLength > 125) { + *ccPtr = [self protocolError:@"all control frames must have a payload length of 125 bytes or less"]; + return nil; + } + NSUInteger offset = 2; + if (payloadLength == 126 && bufferLength >= 4) { + payloadLength = (NSUInteger)([self readUInt16:buffer offset:offset]); + offset += 2; + } else if (payloadLength == 127 && bufferLength >= 10) { + payloadLength = (NSUInteger)([self readUInt64:buffer offset:offset]); + offset += 8; + } + NSUInteger totalSize = offset + payloadLength; + if (bufferLength < totalSize) { + // need more data + return nil; + } + buffer = buffer + offset; // move pointer to payload + if (opcode == LCRTMWebSocketOpcodeConnectionClose) { + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeNoStatusReceived; + if (payloadLength > 1) { + NSInteger closeCode = (NSInteger)([self readUInt16:buffer offset:0]); + if (closeCode > 999 && closeCode < 5000) { + closure.closeCode = closeCode; + } + if (payloadLength > 2) { + NSData *data = [NSData dataWithBytes:buffer + 2 + length:payloadLength - 2]; + closure.reason = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + } + } + *ccPtr = closure; + return nil; } - - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue, ^{ - weakSelf.didDisconnect = NO; - }); + LCRTMWebSocketFrame *frame = [LCRTMWebSocketFrame new]; + frame.isFIN = isFIN; + frame.opcode = opcode; + frame.totalSize = totalSize; + frame.payload = [NSData dataWithBytes:buffer + length:payloadLength]; + return frame; +} - //everything is on a background thread. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - weakSelf.isCreated = YES; - [weakSelf createHTTPRequest]; - weakSelf.isCreated = NO; - }); ++ (LCRTMWebSocketFrame *)frameFrom:(NSData *)data + opcode:(LCRTMWebSocketOpcode)opcode +{ + UInt8 payloadLen; + NSUInteger bufferLength = 6 + data.length; + UInt16 payloadLen16 = 0; + UInt64 payloadLen64 = 0; + if (data.length < 126) { + payloadLen = (UInt8)data.length; + } else if (data.length < UINT16_MAX) { + payloadLen = 126; + bufferLength += 2; + payloadLen16 = (UInt16)data.length; + } else { + payloadLen = 127; + bufferLength += 8; + payloadLen64 = (UInt64)data.length; + } + UInt8 buffer[bufferLength]; + buffer[0] = LCRTMWebSocketFrameBitMaskFIN | opcode; + buffer[1] = LCRTMWebSocketFrameBitMaskMask | payloadLen; + NSUInteger offset = 2; + if (payloadLen16 > 0) { + [self writeUInt16:payloadLen16 buffer:buffer offset:offset]; + offset += 2; + } else if (payloadLen64 > 0) { + [self writeUInt64:payloadLen64 buffer:buffer offset:offset]; + offset += 8; + } + UInt8 *maskingKey = buffer + offset; + __unused int status = SecRandomCopyBytes(kSecRandomDefault, 4, maskingKey); + offset += 4; + for (NSUInteger i = 0; i < data.length; i++) { + buffer[offset + i] = ((UInt8 *)(data.bytes))[i] ^ maskingKey[i % 4]; + } + LCRTMWebSocketFrame *frame = [LCRTMWebSocketFrame new]; + frame.opcode = opcode; + frame.payload = [NSData dataWithBytes:buffer length:bufferLength]; + frame.offset = 0; + return frame; +} + ++ (LCRTMWebSocketFrame *)frameFrom:(LCRTMWebSocketMessage *)message +{ + NSData *data; + LCRTMWebSocketOpcode opcode; + if (message.type == LCRTMWebSocketMessageTypeData) { + data = message.data; + opcode = LCRTMWebSocketOpcodeBinary; + } else { + data = [message.string dataUsingEncoding:NSUTF8StringEncoding]; + opcode = LCRTMWebSocketOpcodeText; + } + return [self frameFrom:data opcode:opcode]; } -///////////////////////////////////////////////////////////////////////////// -- (void)disconnect { - [self writeError:LCRTMCloseCodeNormal]; + ++ (UInt16)readUInt16:(UInt8 *)buffer offset:(NSUInteger)offset +{ + return ((UInt16)(buffer[offset + 0]) << 8) | (UInt16)(buffer[offset + 1]); } -///////////////////////////////////////////////////////////////////////////// -- (void)writeString:(NSString*)string { - if(string) { - [self dequeueWrite:[string dataUsingEncoding:NSUTF8StringEncoding] - withCode:LCRTMOpCodeTextFrame]; + ++ (UInt64)readUInt64:(UInt8 *)buffer offset:(NSUInteger)offset +{ + UInt64 value = 0 | (UInt64)(buffer[offset]); + for (int i = 1; i < 8; i++) { + value = (value << 8) | (UInt64)(buffer[offset + i]); } + return value; } -///////////////////////////////////////////////////////////////////////////// -- (void)writePing:(NSData*)data { - [self dequeueWrite:data withCode:LCRTMOpCodePing]; -} -///////////////////////////////////////////////////////////////////////////// -- (void)writeData:(NSData*)data { - [self dequeueWrite:data withCode:LCRTMOpCodeBinaryFrame]; + ++ (void)writeUInt16:(UInt16)value buffer:(UInt8 *)buffer offset:(NSUInteger)offset +{ + buffer[offset] = (UInt8)(value >> 8); + buffer[offset + 1] = (UInt8)(value & 0xFF); } -///////////////////////////////////////////////////////////////////////////// -- (void)addHeader:(NSString*)value forKey:(NSString*)key { - if(!self.headers) { - self.headers = [[NSMutableDictionary alloc] init]; + ++ (void)writeUInt64:(UInt64)value buffer:(UInt8 *)buffer offset:(NSUInteger)offset +{ + for (int i = 0; i < 8; i++) { + buffer[offset + i] = (UInt8)((value >> (8 * (7 - i))) & 0xFF); } - [self.headers setObject:value forKey:key]; } -///////////////////////////////////////////////////////////////////////////// -#pragma mark - connect's internal supporting methods ++ (LCRTMWebSocketConnectionClosure *)protocolError:(NSString *)reason +{ + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeProtocolError; + closure.reason = reason; + return closure; +} + +@end + +@interface LCRTMWebSocket () -///////////////////////////////////////////////////////////////////////////// +@property (nonatomic) BOOL isOpened; +@property (nonatomic) BOOL isWritable; +@property (nonatomic) dispatch_queue_t readQueue; +@property (nonatomic) dispatch_queue_t writeQueue; +@property (nonatomic) NSInputStream *inputStream; +@property (nonatomic) NSOutputStream *outputStream; +@property (nonatomic) NSMutableData *inputSegmentBuffer; +@property (nonatomic) NSMutableArray *inputFrameStack; +@property (nonatomic) NSMutableArray *outputFrameQueue; -- (NSString *)origin; +@end + +@implementation LCRTMWebSocket + +- (instancetype)init { - NSString *scheme = [self.url.scheme lowercaseString]; - - if ([scheme isEqualToString:@"wss"]) { - scheme = @"https"; - } else if ([scheme isEqualToString:@"ws"]) { - scheme = @"http"; - } - - if (self.url.port) { - return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.url.host, self.url.port]; - } else { - return [NSString stringWithFormat:@"%@://%@/", scheme, self.url.host]; + self = [super init]; + if (self) { + _delegateQueue = dispatch_get_main_queue(); + _isOpened = false; + _isWritable = false; + _readQueue = dispatch_queue_create("LCRTMWebSocket.readQueue", NULL); + _writeQueue = dispatch_queue_create("LCRTMWebSocket.writeQueue", NULL); +#if DEBUG + dispatch_queue_set_specific(_readQueue, + (__bridge void *)_readQueue, + (__bridge void *)_readQueue, + NULL); + dispatch_queue_set_specific(_writeQueue, + (__bridge void *)_writeQueue, + (__bridge void *)_writeQueue, + NULL); +#endif + _inputFrameStack = [NSMutableArray array]; + _outputFrameQueue = [NSMutableArray array]; } + return self; } +- (instancetype)initWithURL:(NSURL *)url +{ + self = [self init]; + if (self) { + _request = [NSMutableURLRequest requestWithURL:url]; + [_request setValue:@"websocket" forHTTPHeaderField:@"Upgrade"]; + [_request setValue:@"Upgrade" forHTTPHeaderField:@"Connection"]; + NSString *origin = url.absoluteString; + NSURL *hostURL = [NSURL URLWithString:@"/" relativeToURL:url]; + if (hostURL) { + origin = hostURL.absoluteString; + origin = [origin substringToIndex:origin.length - 1]; + } + [_request setValue:origin forHTTPHeaderField:@"Origin"]; + [_request setValue:@"13" forHTTPHeaderField:@"Sec-WebSocket-Version"]; + } + return self; +} -//Uses CoreFoundation to build a HTTP request to send over TCP stream. -- (void)createHTTPRequest { - CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)self.url.absoluteString, NULL); - CFStringRef requestMethod = CFSTR("GET"); - CFHTTPMessageRef urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, - requestMethod, - url, - kCFHTTPVersion1_1); - CFRelease(url); - - NSNumber *port = self.url.port; - if (!port) { - if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]){ - port = @(443); - } else { - port = @(80); +- (instancetype)initWithURL:(NSURL *)url + protocols:(NSArray *)protocols +{ + self = [self initWithURL:url]; + if (self) { + if (protocols.count > 0) { + [_request setValue:[protocols componentsJoinedByString:@", "] + forHTTPHeaderField:@"Sec-WebSocket-Protocol"]; } } - NSString *protocols = nil; - if([self.optProtocols count] > 0) { - protocols = [self.optProtocols componentsJoinedByString:@","]; - } - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSHostName, - (__bridge CFStringRef)[NSString stringWithFormat:@"%@:%@",self.url.host,port]); - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSVersionName, - (__bridge CFStringRef)headerWSVersionValue); - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSKeyName, - (__bridge CFStringRef)[self generateWebSocketKey]); - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSUpgradeName, - (__bridge CFStringRef)headerWSUpgradeValue); - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSConnectionName, - (__bridge CFStringRef)headerWSConnectionValue); - if (protocols.length > 0) { - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerWSProtocolName, - (__bridge CFStringRef)protocols); - } - - /// Objc SDK should not set Origin Header. - /* - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)headerOriginName, - (__bridge CFStringRef)[self origin]); - */ - - for(NSString *key in self.headers) { - CFHTTPMessageSetHeaderFieldValue(urlRequest, - (__bridge CFStringRef)key, - (__bridge CFStringRef)self.headers[key]); + return self; +} + +- (instancetype)initWithRequest:(NSURLRequest *)request +{ + self = [self init]; + if (self) { + _request = [request mutableCopy]; } - -#if defined(DEBUG) - NSLog(@"urlRequest = \"%@\"", urlRequest); + return self; +} + +- (BOOL)assertSpecificReadQueue +{ +#if DEBUG + void *specificKey = (__bridge void *)_readQueue; + return dispatch_get_specific(specificKey) == specificKey; +#else + return true; +#endif +} + +- (BOOL)assertSpecificWriteQueue +{ +#if DEBUG + void *specificKey = (__bridge void *)_writeQueue; + return dispatch_get_specific(specificKey) == specificKey; +#else + return true; #endif - NSData *serializedRequest = (__bridge_transfer NSData *)(CFHTTPMessageCopySerializedMessage(urlRequest)); - [self initStreamsWithData:serializedRequest port:port]; - CFRelease(urlRequest); -} -///////////////////////////////////////////////////////////////////////////// -//Random String of 16 lowercase chars, SHA1 and base64 encoded. -- (NSString*)generateWebSocketKey { - NSInteger seed = 16; - NSMutableString *string = [NSMutableString stringWithCapacity:seed]; - for (int i = 0; i < seed; i++) { - [string appendFormat:@"%C", (unichar)('a' + arc4random_uniform(25))]; - } - return [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; -} -///////////////////////////////////////////////////////////////////////////// -//Sets up our reader/writer for the TCP stream. -- (void)initStreamsWithData:(NSData*)data port:(NSNumber*)port { - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.url.host, [port intValue], &readStream, &writeStream); - - self.inputStream = (__bridge_transfer NSInputStream *)readStream; +} + +- (void)open +{ + NSAssert(self.inputStream == nil && self.outputStream == nil, @"should NOT reopen"); + NSURL *url = self.request.URL; + if (!url.host) { + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeInvalid; + closure.reason = @"Invalid request"; + closure.userInfo = @{ + @"URL": url ?: @"nil", + }; + [self notifyCloseWithError:[closure error]]; + return; + } + BOOL isTLS = [LCRTMWebSocket isTLS:url]; + NSNumber *port = url.port; + if (!port) { + port = isTLS ? @443 : @80; + } + NSString *hostWithPort = [NSString stringWithFormat:@"%@:%@", url.host, port]; + if (!self.request.allHTTPHeaderFields[@"Host"]) { + [self.request setValue:hostWithPort + forHTTPHeaderField:@"Host"]; + } + [self.request setValue:[LCRTMWebSocket generateSecWebSocketKey] + forHTTPHeaderField:@"Sec-WebSocket-Key"]; + CFReadStreamRef readStreamRef; + CFWriteStreamRef writeStreamRef; + CFStreamCreatePairWithSocketToHost(NULL, + (__bridge CFStringRef)(url.host), + port.unsignedIntValue, + &readStreamRef, + &writeStreamRef); + self.inputStream = (__bridge_transfer NSInputStream *)readStreamRef; + self.outputStream = (__bridge_transfer NSOutputStream *)writeStreamRef; + if (!self.inputStream || !self.outputStream) { + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeInvalid; + closure.reason = @"Creating sockets failed"; + closure.userInfo = @{ + @"host:port": hostWithPort, + }; + [self notifyCloseWithError:[closure error]]; + return; + } + CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, + self.readQueue); + CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, + self.writeQueue); self.inputStream.delegate = self; - NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream; - self.outputStream = outputStream; self.outputStream.delegate = self; - if([self.url.scheme isEqualToString:@"wss"] || [self.url.scheme isEqualToString:@"https"]) { - [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; - [self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; - } else { - self.certValidated = YES; //not a https session, so no need to check SSL pinning - } - if(self.voipEnabled) { - [self.inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType]; - [self.outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType]; - } - if(self.selfSignedSSL) { - NSString *chain = (__bridge_transfer NSString *)kCFStreamSSLValidatesCertificateChain; - NSString *peerName = (__bridge_transfer NSString *)kCFStreamSSLValidatesCertificateChain; - NSString *key = (__bridge_transfer NSString *)kCFStreamPropertySSLSettings; - NSDictionary *settings = @{chain: [[NSNumber alloc] initWithBool:NO], - peerName: [NSNull null]}; - [self.inputStream setProperty:settings forKey:key]; - [self.outputStream setProperty:settings forKey:key]; - } - CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, self.streamQueue); - CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, self.streamQueue); + if (isTLS) { + [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL + forKey:NSStreamSocketSecurityLevelKey]; + [self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL + forKey:NSStreamSocketSecurityLevelKey]; + } [self.inputStream open]; [self.outputStream open]; - - __weak typeof(self) weakSelf = self; - dispatch_async(self.streamQueue, ^{ - NSTimeInterval timeout = (60 * 1000000); - while (!outputStream.hasSpaceAvailable) { - usleep(100); - timeout -= 100; - if (timeout < 0) { - NSError *error = [LCRTMWebSocket errorWithDetail:@"Timed out waiting for the socket to be ready for a write" code:LCRTMWriteTimeoutError]; - [weakSelf disconnectStream:error]; - return; - } else if (outputStream.streamError) { - [weakSelf disconnectStream:outputStream.streamError]; - return; - } else if (!weakSelf) { - return; - } + __weak typeof(self) ws = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + (int64_t)((self.request.timeoutInterval ?: 60.0) * NSEC_PER_SEC)), + self.readQueue, ^{ + LCRTMWebSocket *ss = ws; + if (!ss) { return; } + if (!ss.isOpened) { + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeInvalid; + closure.reason = @"Opening sockets timeout"; + closure.userInfo = @{ + @"host:port": hostWithPort, + }; + [ss notifyCloseWithError:[closure error]]; + [ss purgeInputResource:true]; + [ss purgeOutputResource:false]; + return; } - NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ - [outputStream write:[data bytes] maxLength:[data length]]; - }]; - [weakSelf.writeQueue addOperation:op]; }); - - self.isRunLoop = YES; - [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - while (self.isRunLoop) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; +} + +- (void)sendMessage:(LCRTMWebSocketMessage *)message + completion:(void (^)(void))completion +{ + dispatch_async(self.writeQueue, ^{ + if (!self.isWritable) { + return; + } + LCRTMWebSocketFrame *frame = [LCRTMWebSocketFrame frameFrom:message]; + frame.completion = completion; + [self.outputFrameQueue addObject:frame]; + [self dequeueFrames]; + }); +} + +- (void)closeWithCloseCode:(LCRTMWebSocketCloseCode)closeCode + reason:(NSData *)reason +{ + NSUInteger bufferLength = 2; + UInt8 buffer[bufferLength]; + [LCRTMWebSocketFrame writeUInt16:(UInt16)closeCode + buffer:buffer offset:0]; + NSMutableData *data = [NSMutableData dataWithBytes:buffer + length:bufferLength]; + if (reason) { + [data appendData:reason]; } + [self sendControlFrames:LCRTMWebSocketOpcodeConnectionClose + data:data completion:nil]; } -///////////////////////////////////////////////////////////////////////////// -#pragma mark - NSStreamDelegate +- (void)sendPing:(NSData *)data + completion:(void (^)(void))completion +{ + [self sendControlFrames:LCRTMWebSocketOpcodePing + data:data + completion:completion]; +} -///////////////////////////////////////////////////////////////////////////// -- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { - if(self.security && !self.certValidated && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { - SecTrustRef trust = (__bridge SecTrustRef)([aStream propertyForKey:(__bridge_transfer NSString *)kCFStreamPropertySSLPeerTrust]); - NSString *domain = [aStream propertyForKey:(__bridge_transfer NSString *)kCFStreamSSLPeerName]; - if([self.security isValid:trust domain:domain]) { - self.certValidated = YES; - } else { - [self disconnectStream:[LCRTMWebSocket errorWithDetail:@"Invalid SSL certificate" code:1]]; +- (void)sendPong:(NSData *)data + completion:(void (^)(void))completion +{ + [self sendControlFrames:LCRTMWebSocketOpcodePong + data:data + completion:completion]; +} + +- (void)sendControlFrames:(LCRTMWebSocketOpcode)opcode + data:(NSData *)data + completion:(void (^)(void))completion +{ + dispatch_async(self.writeQueue, ^{ + if (!self.isWritable) { return; } - } + if (opcode == LCRTMWebSocketOpcodeConnectionClose) { + self.isWritable = false; + } + LCRTMWebSocketFrame *frame = [LCRTMWebSocketFrame frameFrom:data ?: [NSData data] + opcode:opcode]; + frame.completion = completion; + NSUInteger index = (self.outputFrameQueue.firstObject.offset > 0) ? 1 : 0; + [self.outputFrameQueue insertObject:frame + atIndex:index]; + [self dequeueFrames]; + }); +} + +// MARK: NSStreamDelegate + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode +{ switch (eventCode) { - case NSStreamEventNone: - break; - case NSStreamEventOpenCompleted: + [self handleStreamEventOpenCompleted:aStream]; break; - case NSStreamEventHasBytesAvailable: - if(aStream == self.inputStream) { - [self processInputStream]; - } + [self handleStreamEventHasBytesAvailable:aStream]; break; - case NSStreamEventHasSpaceAvailable: + [self handleStreamEventHasSpaceAvailable:aStream]; break; - case NSStreamEventErrorOccurred: - [self disconnectStream:[aStream streamError]]; + [self handleStreamEventErrorOccurred:aStream]; break; - case NSStreamEventEndEncountered: - [self disconnectStream:nil]; + [self handleStreamEventEndEncountered:aStream]; break; - default: break; } } -///////////////////////////////////////////////////////////////////////////// -- (void)disconnectStream:(NSError*)error { - if (error) { - [self.writeQueue cancelAllOperations]; + +- (void)handleStreamEventOpenCompleted:(NSStream *)aStream +{ + if (aStream != self.outputStream) { + return; + } + LCRTMWebSocketFrame *request = [LCRTMWebSocketFrame new]; + request.payload = [LCRTMWebSocket generateHTTPRequestData:self.request]; + request.offset = 0; + [self.outputFrameQueue addObject:request]; + [self dequeueFrames]; +} + +- (void)handleStreamEventHasBytesAvailable:(NSStream *)aStream +{ + if (aStream != self.inputStream) { + return; + } + NSParameterAssert([self assertSpecificReadQueue]); + NSUInteger bufferMax = 1024 * 32; + UInt8 bufferArray[bufferMax]; + NSInteger readBytes = [self.inputStream read:bufferArray + maxLength:bufferMax]; + if (readBytes < 1) { + return; + } + UInt8 *buffer = bufferArray; + NSUInteger bufferLength = (NSUInteger)readBytes; + if (self.inputSegmentBuffer) { + [self.inputSegmentBuffer appendBytes:buffer + length:bufferLength]; + buffer = (UInt8 *)self.inputSegmentBuffer.bytes; + bufferLength = self.inputSegmentBuffer.length; + } + NSUInteger restBufferLength; + if (self.isOpened) { + restBufferLength = [self processDataFrames:buffer + length:bufferLength]; } else { - [self.writeQueue waitUntilAllOperationsAreFinished]; + restBufferLength = [self processHandshake:buffer + length:bufferLength]; } - if (self.inputStream) { - self.inputStream.delegate = nil; - CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, NULL); - [self.inputStream close]; - self.inputStream = nil; + if (restBufferLength == 0) { + self.inputSegmentBuffer = nil; + } else if (restBufferLength != self.inputSegmentBuffer.length) { + self.inputSegmentBuffer = [NSMutableData dataWithBytes:buffer + bufferLength - restBufferLength + length:restBufferLength];; } - if (self.outputStream) { - self.outputStream.delegate = nil; - CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, NULL); - [self.outputStream close]; - self.outputStream = nil; - } - - self.isRunLoop = NO; - self.isConnected = NO; - self.certValidated = NO; - - [self doDisconnect:error]; -} -///////////////////////////////////////////////////////////////////////////// - -#pragma mark - Stream Processing Methods - -///////////////////////////////////////////////////////////////////////////// -- (void)processInputStream { - @autoreleasepool { - uint8_t buffer[BUFFER_MAX]; - NSInteger length = [self.inputStream read:buffer maxLength:BUFFER_MAX]; - if(length > 0) { - if(!self.isConnected) { - CFIndex responseStatusCode; - BOOL status = [self processHTTP:buffer length:length responseStatusCode:&responseStatusCode]; -#if defined(DEBUG) - if (length < BUFFER_MAX) { - buffer[length] = 0x00; - } else { - buffer[BUFFER_MAX - 1] = 0x00; - } - NSLog(@"response (%ld) = \"%s\"", responseStatusCode, buffer); -#endif - if(status == NO) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"Invalid HTTP upgrade" code:1 userInfo:@{@"HTTPResponseStatusCode" : @(responseStatusCode)}]]; - } - } else { - BOOL process = NO; - if(self.inputQueue.count == 0) { - process = YES; - } - [self.inputQueue addObject:[NSData dataWithBytes:buffer length:length]]; - if(process) { - [self dequeueInput]; - } - } - } +} + +- (void)handleStreamEventHasSpaceAvailable:(NSStream *)aStream +{ + if (aStream != self.outputStream) { + return; } + [self dequeueFrames]; } -///////////////////////////////////////////////////////////////////////////// -- (void)dequeueInput { - if(self.inputQueue.count > 0) { - NSData *data = [self.inputQueue objectAtIndex:0]; - NSData *work = data; - if(self.fragBuffer) { - NSMutableData *combine = [NSMutableData dataWithData:self.fragBuffer]; - [combine appendData:data]; - work = combine; - self.fragBuffer = nil; - } - [self processRawMessage:(uint8_t*)work.bytes length:work.length]; - [self.inputQueue removeObject:data]; - [self dequeueInput]; - } -} -///////////////////////////////////////////////////////////////////////////// -//Finds the HTTP Packet in the TCP stream, by looking for the CRLF. -- (BOOL)processHTTP:(uint8_t*)buffer length:(NSInteger)bufferLen responseStatusCode:(CFIndex*)responseStatusCode { - int k = 0; - NSInteger totalSize = 0; - for(int i = 0; i < bufferLen; i++) { - if(buffer[i] == CRLFBytes[k]) { - k++; - if(k == 3) { - totalSize = i + 1; + +- (void)handleStreamEventErrorOccurred:(NSStream *)aStream +{ + NSError *error = aStream.streamError; + NSDictionary *userInfo; + if (error) { + userInfo = @{ + NSUnderlyingErrorKey: error, + }; + } + if (aStream == self.inputStream) { + [self purgeInputResource:true]; + [self purgeOutputResource:false]; + } else { + [self purgeInputResource:false]; + [self purgeOutputResource:true]; + } + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeAbnormalClosure; + closure.reason = @"Error Occurred"; + closure.userInfo = userInfo; + [self notifyCloseWithError:[closure error]]; +} + +- (void)handleStreamEventEndEncountered:(NSStream *)aStream +{ + if (aStream == self.inputStream) { + [self purgeInputResource:true]; + [self closeWithCloseCode:LCRTMWebSocketCloseCodeNormalClosure + reason:nil]; + } else { + [self purgeInputResource:false]; + [self purgeOutputResource:true]; + } + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeInternalServerError; + closure.reason = @"End Encountered"; + closure.userInfo = @{ + @"stream": (aStream == self.inputStream) ? @"input" : @"output", + }; + [self notifyCloseWithError:[closure error]]; +} + +// MARK: Process Data + +- (NSUInteger)processHandshake:(UInt8 *)buffer + length:(NSUInteger)bufferLength +{ + NSParameterAssert([self assertSpecificReadQueue]); + UInt8 CRLFBytes[] = { '\r', '\n', '\r', '\n' }; + NSInteger k = 0; + CFIndex httpResponseSize = 0; + for (int i = 0; i < bufferLength; i++) { + if (buffer[i] == CRLFBytes[k]) { + k += 1; + if (k == 4) { + httpResponseSize = i + 1; break; } } else { k = 0; } } - if(totalSize > 0) { - BOOL status = [self validateResponse:buffer length:totalSize responseStatusCode:responseStatusCode]; - if (status == YES) { - self.isConnected = YES; - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue,^{ - LCRTMWebSocket *strongSelf = weakSelf; - if (!strongSelf) { return; } - if([strongSelf.delegate respondsToSelector:@selector(websocketDidConnect:)]) { - [strongSelf.delegate websocketDidConnect:strongSelf]; - } - }); - totalSize += 1; //skip the last \n - NSInteger restSize = bufferLen-totalSize; - if(restSize > 0) { - [self processRawMessage:(buffer+totalSize) length:restSize]; - } - } - return status; - } - return NO; -} -///////////////////////////////////////////////////////////////////////////// -//Validate the HTTP is a 101, as per the RFC spec. -- (BOOL)validateResponse:(uint8_t *)buffer length:(NSInteger)bufferLen responseStatusCode:(CFIndex*)responseStatusCode { - CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, NO); - CFHTTPMessageAppendBytes(response, buffer, bufferLen); - *responseStatusCode = CFHTTPMessageGetResponseStatusCode(response); - BOOL status = ((*responseStatusCode) == kLCRTMInternalHTTPStatusWebSocket)?(YES):(NO); - if(status == NO) { - CFRelease(response); - return NO; - } - NSDictionary *headers = (__bridge_transfer NSDictionary *)(CFHTTPMessageCopyAllHeaderFields(response)); - NSString *acceptKey = headers[headerWSAcceptName]; - CFRelease(response); - if(acceptKey.length > 0) { - return YES; - } - return NO; -} -///////////////////////////////////////////////////////////////////////////// --(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen { - LCRTMResponse *response = [self.readStack lastObject]; - if(response && bufferLen < 2) { - self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; - return; + if (httpResponseSize == 0) { + return bufferLength; } - if(response.bytesLeft > 0) { - NSInteger len = response.bytesLeft; - NSInteger extra = bufferLen - response.bytesLeft; - if(response.bytesLeft > bufferLen) { - len = bufferLen; - extra = 0; - } - response.bytesLeft -= len; - [response.buffer appendData:[NSData dataWithBytes:buffer length:len]]; - [self processResponse:response]; - NSInteger offset = bufferLen - extra; - if(extra > 0) { - [self processExtra:(buffer+offset) length:extra]; - } - return; - } else { - if(bufferLen < 2) { // we need at least 2 bytes for the header - self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; - return; - } - BOOL isFin = (LCRTMFinMask & buffer[0]); - uint8_t receivedOpcode = (LCRTMOpCodeMask & buffer[0]); - BOOL isMasked = (LCRTMMaskMask & buffer[1]); - uint8_t payloadLen = (LCRTMPayloadLenMask & buffer[1]); - NSInteger offset = 2; //how many bytes do we need to skip for the header - if((isMasked || (LCRTMRSVMask & buffer[0])) && receivedOpcode != LCRTMOpCodePong) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"masked and rsv data is not currently supported" code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - BOOL isControlFrame = (receivedOpcode == LCRTMOpCodeConnectionClose || receivedOpcode == LCRTMOpCodePing); - if(!isControlFrame && (receivedOpcode != LCRTMOpCodeBinaryFrame && receivedOpcode != LCRTMOpCodeContinueFrame && receivedOpcode != LCRTMOpCodeTextFrame && receivedOpcode != LCRTMOpCodePong)) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:[NSString stringWithFormat:@"unknown opcode: 0x%x",receivedOpcode] code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - if(isControlFrame && !isFin) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"control frames can't be fragmented" code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - if(receivedOpcode == LCRTMOpCodeConnectionClose) { - //the server disconnected us - uint16_t code = LCRTMCloseCodeNormal; - if(payloadLen == 1) { - code = LCRTMCloseCodeProtocolError; - } - else if(payloadLen > 1) { - code = CFSwapInt16BigToHost(*(uint16_t *)(buffer+offset) ); - if(code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000)) { - code = LCRTMCloseCodeProtocolError; - } - offset += 2; - } - - if(payloadLen > 2) { - NSInteger len = payloadLen-2; - if(len > 0) { - NSData *data = [NSData dataWithBytes:(buffer+offset) length:len]; - NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if(!str) { - code = LCRTMCloseCodeProtocolError; - } - } - } - [self writeError:code]; - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"continue frame before a binary or text frame" code:code]]; - return; - } - if(isControlFrame && payloadLen > 125) { - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - NSInteger dataLength = payloadLen; - if(payloadLen == 127) { - dataLength = (NSInteger)CFSwapInt64BigToHost(*(uint64_t *)(buffer+offset)); - offset += sizeof(uint64_t); - } else if(payloadLen == 126) { - dataLength = CFSwapInt16BigToHost(*(uint16_t *)(buffer+offset) ); - offset += sizeof(uint16_t); - } - if(bufferLen < offset) { // we cannot process this yet, nead more header data - self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; - return; - } - NSInteger len = dataLength; - if(dataLength > (bufferLen-offset) || (bufferLen - offset) < dataLength) { - len = bufferLen-offset; - } - NSData *data = nil; - if(len < 0) { - len = 0; - data = [NSData data]; + CFHTTPMessageRef messageRef = CFHTTPMessageCreateEmpty(NULL, FALSE); + CFHTTPMessageAppendBytes(messageRef, buffer, httpResponseSize); + CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(messageRef); + NSString *upgrade = ({ + (__bridge_transfer NSString *) + CFHTTPMessageCopyHeaderFieldValue(messageRef, CFSTR("Upgrade")); + }); + NSString *connection = ({ + (__bridge_transfer NSString *) + CFHTTPMessageCopyHeaderFieldValue(messageRef, CFSTR("Connection")); + }); + NSString *secWebSocketAccept = [({ + (__bridge_transfer NSString *) + CFHTTPMessageCopyHeaderFieldValue(messageRef, CFSTR("Sec-WebSocket-Accept")); + }) stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSString *secWebSocketKey = self.request.allHTTPHeaderFields[@"Sec-WebSocket-Key"]; + NSString *requestSecWebSocketProtocol = self.request.allHTTPHeaderFields[@"Sec-WebSocket-Protocol"]; + NSString *responseSecWebSocketProtocol = ({ + (__bridge_transfer NSString *) + CFHTTPMessageCopyHeaderFieldValue(messageRef, CFSTR("Sec-WebSocket-Protocol")); + }); + CFRelease(messageRef); + if (statusCode != 101) { + [self purgeInputResource:true]; + [self purgeOutputResource:false]; + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = statusCode; + closure.reason = @"Upgrade failed, status code is not `101`."; + [self notifyCloseWithError:[closure error]]; + return 0; + } + if ([upgrade caseInsensitiveCompare:@"websocket"] != NSOrderedSame + || [connection caseInsensitiveCompare:@"upgrade"] != NSOrderedSame + || ![LCRTMWebSocket validateSecWebSocketAccept:secWebSocketAccept + secWebSocketKey:secWebSocketKey] + || ![LCRTMWebSocket validateSecWebSocketProtocol:requestSecWebSocketProtocol + responseProtocol:responseSecWebSocketProtocol]) { + [self purgeInputResource:true]; + [self purgeOutputResource:false]; + LCRTMWebSocketConnectionClosure *closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeInvalid; + closure.reason = @"Upgrade failed, response headers invalid."; + closure.userInfo = @{ + @"Upgrade": upgrade ?: @"nil", + @"Connection": connection ?: @"nil", + @"Sec-WebSocket-Accept": secWebSocketAccept ?: @"nil", + @"Sec-WebSocket-Key": secWebSocketKey ?: @"nil", + @"Request-Sec-WebSocket-Protocol": requestSecWebSocketProtocol ?: @"nil", + @"Response-Sec-WebSocket-Protocol": responseSecWebSocketProtocol ?: @"nil", + }; + [self notifyCloseWithError:[closure error]]; + return 0; + } + self.isOpened = true; + [self async:self.writeQueue block:^(LCRTMWebSocket *client) { + client.isWritable = true; + }]; + [self async:self.delegateQueue block:^(LCRTMWebSocket *client) { + [client.delegate LCRTMWebSocket:client + didOpenWithProtocol:responseSecWebSocketProtocol]; + }]; + return [self processDataFrames:buffer + httpResponseSize + length:bufferLength - httpResponseSize]; +} + +- (NSUInteger)processDataFrames:(uint8_t *)buffer + length:(NSUInteger)bufferLength +{ + NSParameterAssert([self assertSpecificReadQueue]); + if (bufferLength < 2) { + return bufferLength; + } + LCRTMWebSocketConnectionClosure *closure; + LCRTMWebSocketFrame *frame = [LCRTMWebSocketFrame frameFrom:buffer + length:bufferLength + connectionClosure:&closure]; + if (closure) { + if (closure.closeCode == LCRTMWebSocketCloseCodeProtocolError) { + [self closeWithCloseCode:closure.closeCode + reason:nil]; } else { - data = [NSData dataWithBytes:(buffer+offset) length:len]; - } - if(receivedOpcode == LCRTMOpCodePong) { - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue,^{ - LCRTMWebSocket *strongSelf = weakSelf; - if (!strongSelf) { return; } - if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceivePong:)]) { - [strongSelf.delegate websocket:strongSelf didReceivePong:data]; + [self closeWithCloseCode:LCRTMWebSocketCloseCodeNormalClosure + reason:nil]; + } + [self notifyCloseWithError:[closure error]]; + return 0; + } + if (!frame) { + return bufferLength; + } + NSUInteger offset = frame.totalSize; + if (frame.isFIN) { + if (frame.opcode == LCRTMWebSocketOpcodeBinary + || frame.opcode == LCRTMWebSocketOpcodeText + || frame.opcode == LCRTMWebSocketOpcodeContinuation) { + if (frame.opcode == LCRTMWebSocketOpcodeContinuation) { + if (self.inputFrameStack.count > 0) { + [self.inputFrameStack addObject:frame]; + LCRTMWebSocketFrame *completeFrame = [LCRTMWebSocketFrame new]; + completeFrame.opcode = self.inputFrameStack.firstObject.opcode; + NSMutableData *payload = [NSMutableData data]; + for (LCRTMWebSocketFrame *item in self.inputFrameStack) { + [payload appendData:item.payload]; + } + completeFrame.payload = payload; + frame = completeFrame; + [self.inputFrameStack removeAllObjects]; + } else { + closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeProtocolError; + closure.reason = @"Message fragments NOT be sent in the order by server."; + [self closeWithCloseCode:closure.closeCode + reason:nil]; + [self notifyCloseWithError:[closure error]]; + return 0; } - }); - NSInteger step = (offset+len); - NSInteger extra = bufferLen-step; - if(extra > 0) { - [self processRawMessage:(buffer+step) length:extra]; } - return; - } - LCRTMResponse *response = [self.readStack lastObject]; - if(isControlFrame) { - response = nil; //don't append pings - } - if(!isFin && receivedOpcode == LCRTMOpCodeContinueFrame && !response) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"continue frame before a binary or text frame" code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - BOOL isNew = NO; - if(!response) { - if(receivedOpcode == LCRTMOpCodeContinueFrame) { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"first frame can't be a continue frame" code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; - } - isNew = YES; - response = [LCRTMResponse new]; - response.code = receivedOpcode; - response.bytesLeft = dataLength; - response.buffer = [NSMutableData dataWithData:data]; - } else { - if(receivedOpcode == LCRTMOpCodeContinueFrame) { - response.bytesLeft = dataLength; + LCRTMWebSocketMessage *message; + if (frame.opcode == LCRTMWebSocketOpcodeBinary) { + message = [LCRTMWebSocketMessage messageWithData:frame.payload]; } else { - [self doDisconnect:[LCRTMWebSocket errorWithDetail:@"second and beyond of fragment message must be a continue frame" code:LCRTMCloseCodeProtocolError]]; - [self writeError:LCRTMCloseCodeProtocolError]; - return; + message = [LCRTMWebSocketMessage + messageWithString:[[NSString alloc] + initWithData:frame.payload + encoding:NSUTF8StringEncoding]]; } - [response.buffer appendData:data]; - } - response.bytesLeft -= len; - response.frameCount++; - response.isFin = isFin; - if(isNew) { - [self.readStack addObject:response]; + [self async:self.delegateQueue block:^(LCRTMWebSocket *client) { + [client.delegate LCRTMWebSocket:client + didReceiveMessage:message]; + }]; + } else if (frame.opcode == LCRTMWebSocketOpcodePong) { + [self async:self.delegateQueue block:^(LCRTMWebSocket *client) { + [client.delegate LCRTMWebSocket:client + didReceivePong:frame.payload]; + }]; + } else if (frame.opcode == LCRTMWebSocketOpcodePing) { + [self async:self.delegateQueue block:^(LCRTMWebSocket *client) { + [client.delegate LCRTMWebSocket:client + didReceivePing:frame.payload]; + }]; } - [self processResponse:response]; - - NSInteger step = (offset+len); - NSInteger extra = bufferLen-step; - if(extra > 0) { - [self processExtra:(buffer+step) length:extra]; - } - } - -} -///////////////////////////////////////////////////////////////////////////// -- (void)processExtra:(uint8_t*)buffer length:(NSInteger)bufferLen { - if(bufferLen < 2) { - self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen]; } else { - [self processRawMessage:buffer length:bufferLen]; - } -} -///////////////////////////////////////////////////////////////////////////// -- (BOOL)processResponse:(LCRTMResponse*)response { - if(response.isFin && response.bytesLeft <= 0) { - NSData *data = response.buffer; - if(response.code == LCRTMOpCodePing) { - [self dequeueWrite:response.buffer withCode:LCRTMOpCodePong]; - } else if(response.code == LCRTMOpCodeTextFrame) { - NSString *str = [[NSString alloc] initWithData:response.buffer encoding:NSUTF8StringEncoding]; - if(!str) { - [self writeError:LCRTMCloseCodeEncoding]; - return NO; - } - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue,^{ - LCRTMWebSocket *strongSelf = weakSelf; - if (!strongSelf) { return; } - if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceiveMessage:)]) { - [strongSelf.delegate websocket:strongSelf didReceiveMessage:str]; - } - }); - } else if(response.code == LCRTMOpCodeBinaryFrame) { - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue,^{ - LCRTMWebSocket *strongSelf = weakSelf; - if (!strongSelf) { return; } - if([strongSelf.delegate respondsToSelector:@selector(websocket:didReceiveData:)]) { - [strongSelf.delegate websocket:strongSelf didReceiveData:data]; - } - }); + if ((self.inputFrameStack.count > 0 + && frame.opcode == LCRTMWebSocketOpcodeContinuation) + || (self.inputFrameStack.count == 0 + && (frame.opcode == LCRTMWebSocketOpcodeBinary + || frame.opcode == LCRTMWebSocketOpcodeText))) { + [self.inputFrameStack addObject:frame]; + } else { + closure = [LCRTMWebSocketConnectionClosure new]; + closure.closeCode = LCRTMWebSocketCloseCodeProtocolError; + closure.reason = @"Message fragments NOT be sent in the order by server."; + [self closeWithCloseCode:closure.closeCode + reason:nil]; + [self notifyCloseWithError:[closure error]]; + return 0; } - [self.readStack removeLastObject]; - return YES; } - return NO; + return [self processDataFrames:buffer + offset + length:bufferLength - offset]; } -///////////////////////////////////////////////////////////////////////////// --(void)dequeueWrite:(NSData*)data withCode:(LCRTMOpCode)code { - if(!self.isConnected) { + +- (void)dequeueFrames +{ + NSParameterAssert([self assertSpecificWriteQueue]); + LCRTMWebSocketFrame *frame = self.outputFrameQueue.firstObject; + if (!frame) { return; } - if(!self.writeQueue) { - self.writeQueue = [[NSOperationQueue alloc] init]; - self.writeQueue.maxConcurrentOperationCount = 1; + NSInteger writtenBytes = [self.outputStream write:(UInt8 *)(frame.payload.bytes) + frame.offset + maxLength:frame.payload.length - frame.offset]; + if (writtenBytes < 1) { + return; } - - __weak typeof(self) weakSelf = self; - [self.writeQueue addOperationWithBlock:^{ - LCRTMWebSocket *strongSelf = weakSelf; - if(!strongSelf || !strongSelf.isConnected) { - return; - } - uint64_t offset = 2; //how many bytes do we need to skip for the header - uint8_t *bytes = (uint8_t*)[data bytes]; - uint64_t dataLength = data.length; - NSMutableData *frame = [[NSMutableData alloc] initWithLength:(NSInteger)(dataLength + LCRTMMaxFrameSize)]; - uint8_t *buffer = (uint8_t*)[frame mutableBytes]; - buffer[0] = LCRTMFinMask | code; - if(dataLength < 126) { - buffer[1] |= dataLength; - } else if(dataLength <= UINT16_MAX) { - buffer[1] |= 126; - *((uint16_t *)(buffer + offset)) = CFSwapInt16BigToHost((uint16_t)dataLength); - offset += sizeof(uint16_t); - } else { - buffer[1] |= 127; - *((uint64_t *)(buffer + offset)) = CFSwapInt64BigToHost((uint64_t)dataLength); - offset += sizeof(uint64_t); + frame.offset += (NSUInteger)writtenBytes; + if (frame.offset == frame.payload.length) { + if (frame.completion) { + frame.completion(); } - BOOL isMask = YES; - if(isMask) { - buffer[1] |= LCRTMMaskMask; - uint8_t *mask_key = (buffer + offset); - (void)SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); - offset += sizeof(uint32_t); - - for (size_t i = 0; i < dataLength; i++) { - buffer[offset] = bytes[i] ^ mask_key[i % sizeof(uint32_t)]; - offset += 1; - } + if (frame.opcode == LCRTMWebSocketOpcodeConnectionClose) { + [self purgeOutputResource:true]; } else { - for(size_t i = 0; i < dataLength; i++) { - buffer[offset] = bytes[i]; - offset += 1; - } - } - uint64_t total = 0; - while (true) { - if(!strongSelf.isConnected || !strongSelf.outputStream) { - break; - } - NSInteger len = [strongSelf.outputStream write:([frame bytes]+total) maxLength:(NSInteger)(offset-total)]; - if(len < 0 || len == NSNotFound) { - NSError *error = strongSelf.outputStream.streamError; - if(!error) { - error = [LCRTMWebSocket errorWithDetail:@"output stream error during write" code:LCRTMOutputStreamWriteError]; - } - [strongSelf doDisconnect:error]; - break; - } else { - total += len; - } - if(total >= offset) { - break; - } + [self.outputFrameQueue removeObjectAtIndex:0]; } - }]; -} -///////////////////////////////////////////////////////////////////////////// -- (void)doDisconnect:(NSError*)error { - if(!self.didDisconnect) { - self.didDisconnect = YES; - __weak typeof(self) weakSelf = self; - dispatch_async(self.queue, ^{ - LCRTMWebSocket *strongSelf = weakSelf; - if (!strongSelf) { return; } - - [strongSelf disconnect]; - - if([strongSelf.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) { - [strongSelf.delegate websocketDidDisconnect:strongSelf error:error]; - } - }); } } -///////////////////////////////////////////////////////////////////////////// -+ (NSError *)errorWithDetail:(NSString *)detail code:(NSInteger)code { - return [self errorWithDetail:detail code:code userInfo:nil]; + +// MARK: Misc + ++ (BOOL)isTLS:(NSURL *)url +{ + NSString *scheme = url.scheme; + return scheme.length > 0 && [@[@"wss", @"https"] containsObject:scheme.lowercaseString]; } -+ (NSError *)errorWithDetail:(NSString *)detail code:(NSInteger)code userInfo:(NSDictionary *)userInfo { - NSMutableDictionary *info; - if (userInfo) { - info = [NSMutableDictionary dictionaryWithDictionary:userInfo]; + ++ (NSString *)generateSecWebSocketKey +{ + NSMutableData *bytes = [[NSMutableData alloc] initWithLength:16]; + if (SecRandomCopyBytes(kSecRandomDefault, bytes.length, bytes.mutableBytes) == errSecSuccess) { + return [bytes base64EncodedStringWithOptions:0]; } else { - info = [NSMutableDictionary dictionary]; + NSInteger seed = 16; + NSMutableString *string = [NSMutableString stringWithCapacity:seed]; + for (int i = 0; i < seed; i++) { + [string appendFormat:@"%C", (unichar)(97 + arc4random_uniform(25))]; + } + return [[string dataUsingEncoding:NSUTF8StringEncoding] + base64EncodedStringWithOptions:0]; } - info[NSLocalizedFailureReasonErrorKey] = detail; - return [NSError errorWithDomain:@"LCRTMWebSocket" code:code userInfo:info]; -} -///////////////////////////////////////////////////////////////////////////// -- (void)writeError:(uint16_t)code { - uint16_t buffer[1]; - buffer[0] = CFSwapInt16BigToHost(code); - [self dequeueWrite:[NSData dataWithBytes:buffer length:sizeof(uint16_t)] withCode:LCRTMOpCodeConnectionClose]; } -///////////////////////////////////////////////////////////////////////////// -- (void)dealloc { - if(self.isConnected) { - [self disconnect]; + ++ (BOOL)validateSecWebSocketAccept:(NSString *)value + secWebSocketKey:(NSString *)key +{ + if (!value.length || !key.length) { + return false; } + NSData *data = [[key stringByAppendingString:@"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"] + dataUsingEncoding:NSUTF8StringEncoding]; + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; + CC_SHA1(data.bytes, (CC_LONG)data.length, digest); + return [[[NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH] + base64EncodedStringWithOptions:0] + isEqualToString:value]; } -///////////////////////////////////////////////////////////////////////////// -@end - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -@implementation LCRTMResponse - -@end -///////////////////////////////////////////////////////////////////////////// - -@protocol LCRTMFoundationTransportDelegate - -- (void)connected; -- (void)cancelled; -- (void)failedWithError:(NSError *)error; -- (void)receive:(NSData *)data; - -@end - -@interface LCRTMFoundationTransport : NSObject -@property (nonatomic, weak) id delegate; -@property (nonatomic, strong) dispatch_queue_t delegateQueue; -@property (nonatomic, strong) dispatch_queue_t workQueue; -@property (nonatomic, strong) dispatch_queue_t writeQueue; -@property (nonatomic, strong) NSInputStream *inputStream; -@property (nonatomic, strong) NSOutputStream *outputStream; -@property (nonatomic, assign) BOOL isOpened; - -@end - -@implementation LCRTMFoundationTransport - -- (instancetype)init ++ (BOOL)validateSecWebSocketProtocol:(NSString *)requestProtocol + responseProtocol:(NSString *)responseProtocol { - self = [super init]; - if (self) { - _delegateQueue = dispatch_get_main_queue(); - _workQueue = dispatch_queue_create("LCRTMFoundationTransport.workQueue", NULL); - _writeQueue = dispatch_queue_create("LCRTMFoundationTransport.writeQueue", NULL); - _isOpened = false; + NSMutableSet *requestProtocolSet = [NSMutableSet set]; + if (requestProtocol.length > 0) { + NSArray *requestProtocolArray = [requestProtocol componentsSeparatedByString:@","]; + for (NSString *item in requestProtocolArray) { + [requestProtocolSet addObject: + [item stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]]; + } + } + if (responseProtocol.length > 0) { + NSArray *responseProtocolArray = [responseProtocol componentsSeparatedByString:@","]; + for (NSString *item in responseProtocolArray) { + if (![requestProtocolSet containsObject: + [item stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]]) { + return false; + } + } } - return self; + return true; } -- (void)connectURL:(NSURL *)URL timeout:(NSTimeInterval)timeout ++ (NSData *)generateHTTPRequestData:(NSURLRequest *)request { - if (!URL.host) { - dispatch_async(self.delegateQueue, ^{ - [self.delegate failedWithError:LCError(9976, @"Invalid request.", @{ - @"URL": (URL ?: @"nil"), - })]; - }); - return; - } - NSNumber *port = URL.port; - if (!port) { - if (URL.scheme && [@[@"wss", @"https"] containsObject:URL.scheme]) { - port = @443; - } else { - port = @80; - } + CFHTTPMessageRef messageRef = CFHTTPMessageCreateRequest(NULL, + CFSTR("GET"), + (__bridge CFURLRef)request.URL, + kCFHTTPVersion1_1); + for (NSString *key in request.allHTTPHeaderFields) { + NSString *value = request.allHTTPHeaderFields[key]; + CFHTTPMessageSetHeaderFieldValue(messageRef, + (__bridge CFStringRef)key, + (__bridge CFStringRef)value); } - CFReadStreamRef readStreamRef; - CFWriteStreamRef writeStreamRef; - CFStreamCreatePairWithSocketToHost(NULL, - (__bridge CFStringRef)(URL.host), - port.unsignedIntValue, - &readStreamRef, - &writeStreamRef); - self.inputStream = (__bridge_transfer NSInputStream *)(readStreamRef); - self.outputStream = (__bridge_transfer NSOutputStream *)(writeStreamRef); - if (!self.inputStream || !self.outputStream) { - dispatch_async(self.delegateQueue, ^{ - [self.delegate failedWithError:LCError(9976, @"Socket creating failed.", @{ - @"host": URL.host, - @"port": port, - })]; - }); - return; + if (request.HTTPBody) { + CFHTTPMessageSetBody(messageRef, + (__bridge CFDataRef)request.HTTPBody); } - self.inputStream.delegate = self; - self.outputStream.delegate = self; - self.isOpened = false; - CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, self.workQueue); - CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, self.workQueue); - [self.inputStream open]; - [self.outputStream open]; - __weak typeof(self) ws = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), self.workQueue, ^{ - LCRTMFoundationTransport *ss = ws; - if (!ss) { - return; - } - if (!ss.isOpened) { - dispatch_async(self.delegateQueue, ^{ - [ss.delegate failedWithError:LCError(9001, @"Socket opening timeout.", @{ - @"host": URL.host, - @"port": port, - })]; - }); - return; - } - }); + NSData *data = (__bridge_transfer NSData *)CFHTTPMessageCopySerializedMessage(messageRef); + CFRelease(messageRef); + return data; } -- (void)disconnect +- (void)notifyCloseWithError:(NSError *)error { - dispatch_async(self.workQueue, ^{ - [self _disconnect]; - }); + [self async:self.delegateQueue block:^(LCRTMWebSocket *client) { + [client.delegate LCRTMWebSocket:client + didCloseWithError:error]; + }]; } -- (void)_disconnect +- (void)safeClean { - if (self.inputStream) { + [self purgeInputResource:false]; + [self purgeOutputResource:false]; +} + +- (void)purgeInputResource:(BOOL)inCurrentQueue +{ + void(^purge)(void) = ^(void) { + NSParameterAssert([self assertSpecificReadQueue]); + self.isOpened = false; self.inputStream.delegate = nil; CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef)self.inputStream, NULL); [self.inputStream close]; - self.inputStream = nil; + self.inputSegmentBuffer = nil; + [self.inputFrameStack removeAllObjects]; + }; + if (inCurrentQueue) { + purge(); + } else { + dispatch_async(self.readQueue, ^{ + purge(); + }); } - if (self.outputStream) { +} + +- (void)purgeOutputResource:(BOOL)inCurrentQueue +{ + void(^purge)(void) = ^(void) { + NSParameterAssert([self assertSpecificWriteQueue]); + self.isWritable = false; self.outputStream.delegate = nil; CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef)self.outputStream, NULL); [self.outputStream close]; - self.outputStream = nil; + [self.outputFrameQueue removeAllObjects]; + }; + if (inCurrentQueue) { + purge(); + } else { + dispatch_async(self.writeQueue, ^{ + purge(); + }); } - self.isOpened = false; -} - -- (void)write:(NSData *)data error:(NSError * __autoreleasing *)errPtr -{ - } -- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode +- (void)async:(dispatch_queue_t)queue + block:(void(^)(LCRTMWebSocket *client))block { - + __weak typeof(self) ws = self; + dispatch_async(queue, ^{ + LCRTMWebSocket *ss = ws; + if (ss) { + block(ss); + } + }); } @end From f8857a68640b8e056d8f20966793c521409eb797 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 24 Apr 2020 15:53:43 +0800 Subject: [PATCH 20/21] test: update test server --- AVOS/AVOSCloudIMTests/Swift/AVIMClientTestCase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AVOS/AVOSCloudIMTests/Swift/AVIMClientTestCase.swift b/AVOS/AVOSCloudIMTests/Swift/AVIMClientTestCase.swift index 00899db7e..4cae68c01 100644 --- a/AVOS/AVOSCloudIMTests/Swift/AVIMClientTestCase.swift +++ b/AVOS/AVOSCloudIMTests/Swift/AVIMClientTestCase.swift @@ -344,7 +344,7 @@ class AVIMClientTestCase: LCIMTestBase { } func test_goaway() { - AVOSCloudIM.defaultOptions().rtmServer = "wss://rtm51.leancloud.cn"; + AVOSCloudIM.defaultOptions().rtmServer = "wss://cn-n1-core-k8s-cell-12.leancloud.cn"; guard let client: AVIMClient = LCIMTestBase.newOpenedClient(clientId: String(#function[..<#function.firstIndex(of: "(")!])) else { XCTFail() From ade3893b09efaffd64349ae29c317b39607e386d Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 24 Apr 2020 16:00:00 +0800 Subject: [PATCH 21/21] release: 12.2.0-beta.1 --- AVOS/AVOSCloud/UserAgent.h | 2 +- AVOSCloud.podspec | 2 +- AVOSCloudIM.podspec | 2 +- AVOSCloudLiveQuery.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AVOS/AVOSCloud/UserAgent.h b/AVOS/AVOSCloud/UserAgent.h index 69de5196c..24b1424ad 100644 --- a/AVOS/AVOSCloud/UserAgent.h +++ b/AVOS/AVOSCloud/UserAgent.h @@ -1 +1 @@ -#define SDK_VERSION @"12.1.3" +#define SDK_VERSION @"12.2.0-beta.1" diff --git a/AVOSCloud.podspec b/AVOSCloud.podspec index 0f4808eb8..7167a9e8f 100644 --- a/AVOSCloud.podspec +++ b/AVOSCloud.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloud' - s.version = '12.1.3' + s.version = '12.2.0-beta.1' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud Objective-C SDK' s.authors = 'LeanCloud' diff --git a/AVOSCloudIM.podspec b/AVOSCloudIM.podspec index 1c5763006..cb726f96d 100644 --- a/AVOSCloudIM.podspec +++ b/AVOSCloudIM.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloudIM' - s.version = '12.1.3' + s.version = '12.2.0-beta.1' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud IM Objective-C SDK' s.authors = 'LeanCloud' diff --git a/AVOSCloudLiveQuery.podspec b/AVOSCloudLiveQuery.podspec index be646aaf1..1b923cbdb 100644 --- a/AVOSCloudLiveQuery.podspec +++ b/AVOSCloudLiveQuery.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVOSCloudLiveQuery' - s.version = '12.1.3' + s.version = '12.2.0-beta.1' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud LiveQuery Objective-C SDK' s.authors = 'LeanCloud'