diff --git a/MASFoundation.xcodeproj/project.pbxproj b/MASFoundation.xcodeproj/project.pbxproj index f77cc0e4..8c947746 100644 --- a/MASFoundation.xcodeproj/project.pbxproj +++ b/MASFoundation.xcodeproj/project.pbxproj @@ -187,6 +187,10 @@ C81CC3CD1FC2EA190058718E /* MASBrowserBasedAuthentication.m in Sources */ = {isa = PBXBuildFile; fileRef = C81CC3CB1FC2EA190058718E /* MASBrowserBasedAuthentication.m */; }; C81CC3D01FC2EFBB0058718E /* UIAlertController+MAS.h in Headers */ = {isa = PBXBuildFile; fileRef = C81CC3CE1FC2EFBB0058718E /* UIAlertController+MAS.h */; }; C81CC3D11FC2EFBB0058718E /* UIAlertController+MAS.m in Sources */ = {isa = PBXBuildFile; fileRef = C81CC3CF1FC2EFBB0058718E /* UIAlertController+MAS.m */; }; + C858D6B52398FC5400963763 /* MASDataTask+MASPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = C858D6B32398FC5400963763 /* MASDataTask+MASPrivate.h */; }; + C858D6B62398FC5400963763 /* MASDataTask+MASPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = C858D6B42398FC5400963763 /* MASDataTask+MASPrivate.m */; }; + C8B1700B238FD1A7007BB903 /* MASDataTask.h in Headers */ = {isa = PBXBuildFile; fileRef = C8B17009238FD1A7007BB903 /* MASDataTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C8B1700C238FD1A7007BB903 /* MASDataTask.m in Sources */ = {isa = PBXBuildFile; fileRef = C8B1700A238FD1A7007BB903 /* MASDataTask.m */; }; C8C32B0D22D706B900D64DF0 /* MASMultiPartFormData.h in Headers */ = {isa = PBXBuildFile; fileRef = C8C32B0C22D706B900D64DF0 /* MASMultiPartFormData.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8C32B1022D7163100D64DF0 /* MASMultiPartRequestSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = C8C32B0E22D7163100D64DF0 /* MASMultiPartRequestSerializer.h */; }; C8C32B1122D7163100D64DF0 /* MASMultiPartRequestSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = C8C32B0F22D7163100D64DF0 /* MASMultiPartRequestSerializer.m */; }; @@ -569,6 +573,10 @@ C81CC3CB1FC2EA190058718E /* MASBrowserBasedAuthentication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MASBrowserBasedAuthentication.m; sourceTree = ""; }; C81CC3CE1FC2EFBB0058718E /* UIAlertController+MAS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertController+MAS.h"; sourceTree = ""; }; C81CC3CF1FC2EFBB0058718E /* UIAlertController+MAS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertController+MAS.m"; sourceTree = ""; }; + C858D6B32398FC5400963763 /* MASDataTask+MASPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MASDataTask+MASPrivate.h"; sourceTree = ""; }; + C858D6B42398FC5400963763 /* MASDataTask+MASPrivate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "MASDataTask+MASPrivate.m"; sourceTree = ""; }; + C8B17009238FD1A7007BB903 /* MASDataTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MASDataTask.h; sourceTree = ""; }; + C8B1700A238FD1A7007BB903 /* MASDataTask.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MASDataTask.m; sourceTree = ""; }; C8C32B0C22D706B900D64DF0 /* MASMultiPartFormData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MASMultiPartFormData.h; sourceTree = ""; }; C8C32B0E22D7163100D64DF0 /* MASMultiPartRequestSerializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MASMultiPartRequestSerializer.h; sourceTree = ""; }; C8C32B0F22D7163100D64DF0 /* MASMultiPartRequestSerializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MASMultiPartRequestSerializer.m; sourceTree = ""; }; @@ -1112,6 +1120,8 @@ 699570E52062FF1300017244 /* MASError.m */, 699570E02060650400017244 /* MASNotifications.h */, 699570E12060650400017244 /* MASNotifications.m */, + C8B17009238FD1A7007BB903 /* MASDataTask.h */, + C8B1700A238FD1A7007BB903 /* MASDataTask.m */, 107389F21C7118F800B7E87E /* MQTT */, A419B39A1C17622E008DC88C /* categories */, A4831A9A1BD1A551007B4AE6 /* models */, @@ -1267,6 +1277,8 @@ CB3173DF1F1FFF2C00C85E47 /* MASNetworkMonitor.m */, CBBA9A7E20742BA800BB307F /* MASNetworkReachability.h */, CBBA9A7F20742BA800BB307F /* MASNetworkReachability.m */, + C858D6B32398FC5400963763 /* MASDataTask+MASPrivate.h */, + C858D6B42398FC5400963763 /* MASDataTask+MASPrivate.m */, ); path = internal; sourceTree = ""; @@ -1551,6 +1563,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C8B1700B238FD1A7007BB903 /* MASDataTask.h in Headers */, C8C32B0D22D706B900D64DF0 /* MASMultiPartFormData.h in Headers */, CB2A4048209A341A00F988AA /* MASMultiFactorHandler.h in Headers */, CB1FD14B1FB23701000AFA25 /* MASSharedStorage.h in Headers */, @@ -1631,6 +1644,7 @@ A46F49D31C2F5FC500A4C370 /* MASIURLRequestSerialization.h in Headers */, A4831AB11BD1A551007B4AE6 /* MASFile.h in Headers */, 699570E22060650400017244 /* MASNotifications.h in Headers */, + C858D6B52398FC5400963763 /* MASDataTask+MASPrivate.h in Headers */, 1059D3761B61AA3800223267 /* MASFoundation.h in Headers */, A898EF6B2182D3DF00CF291B /* NSURLSession+MASPrivate.h in Headers */, A46F49CD1C2F5FC500A4C370 /* MASINetworkReachabilityManager.h in Headers */, @@ -1783,6 +1797,7 @@ TargetAttributes = { 1059D36F1B61AA3700223267 = { CreatedOnToolsVersion = 6.4; + DevelopmentTeam = 2JJZ96P8P5; ProvisioningStyle = Manual; }; 1059D37A1B61AA3800223267 = { @@ -1955,6 +1970,7 @@ CB9975471EDF5799006CEBB1 /* MASAuthCredentials+MASPrivate.m in Sources */, CB273F011DF8C5BF00AF2BDB /* L7SBrowserURLProtocol.m in Sources */, CB9975531EDF591C006CEBB1 /* MASAuthCredentialsJWT.m in Sources */, + C858D6B62398FC5400963763 /* MASDataTask+MASPrivate.m in Sources */, CBD25B041E78C47C00DFB47F /* JWTCoding+VersionOne.m in Sources */, CB9B1208210949D5008A2075 /* NSMutableData+MASASN1Helper.m in Sources */, CB99754B1EDF57D6006CEBB1 /* MASAuthCredentialsPassword.m in Sources */, @@ -1979,6 +1995,7 @@ CB6491FA1FE9DAF300281288 /* MQTTSessionSynchron.m in Sources */, A4831AB01BD1A551007B4AE6 /* MASDevice.m in Sources */, CBD25B001E78C47C00DFB47F /* JWTClaimsSetVerifier.m in Sources */, + C8B1700C238FD1A7007BB903 /* MASDataTask.m in Sources */, A46F49E31C2F5FC500A4C370 /* UIImageView+MASINetworking.m in Sources */, A421571C1BF863480034BDC9 /* MASServiceRegistry.m in Sources */, CBD25AEB1E78C47C00DFB47F /* JWTAlgorithmDataHolder.m in Sources */, @@ -2066,7 +2083,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; @@ -2115,7 +2132,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; @@ -2143,13 +2160,13 @@ 1059D3871B61AA3800223267 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2.0.00; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2JJZ96P8P5; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -2191,13 +2208,13 @@ 1059D3881B61AA3800223267 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2.0.00; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2JJZ96P8P5; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -2239,7 +2256,7 @@ 1059D38A1B61AA3800223267 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = 2JJZ96P8P5; FRAMEWORK_SEARCH_PATHS = ( @@ -2262,7 +2279,7 @@ 1059D38B1B61AA3800223267 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = 2JJZ96P8P5; FRAMEWORK_SEARCH_PATHS = ( @@ -2281,7 +2298,7 @@ 1059D3BA1B61C00500223267 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; ENABLE_BITCODE = YES; MACH_O_TYPE = staticlib; @@ -2293,7 +2310,7 @@ 1059D3BB1B61C00500223267 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; ENABLE_BITCODE = YES; MACH_O_TYPE = staticlib; diff --git a/MASFoundation/Classes/MAS.h b/MASFoundation/Classes/MAS.h index 1c4e6e3c..4222d408 100644 --- a/MASFoundation/Classes/MAS.h +++ b/MASFoundation/Classes/MAS.h @@ -1059,6 +1059,32 @@ withParameters:(NSDictionary *_Nullable)parameterInfo + (void)invoke:(nonnull MASRequest *)request completion:(nullable MASResponseObjectErrorBlock)completion; +/** +* Invoke the endpoint with the parameters defined in the MASRequest object and also cancel a pending request using the taskBlock +* +* If endPointPath is full URL format (including port number and http protocol), SDK will validate the server from the client side through SSL pinning (authentication challenge) with +* provided subjectKeyHash (also known as public key hash) in configuration in mag.mobile_sdk.trusted_cert_pinned_public_key_hashes and mag.mobile_sdk.enable_public_key_pinning. +* ALL of servers' public key hashes in certificate chain must be defined in the list. This means when it is configured to use public key hash pinning for SSL pinning, +* subjectKeyHash (public key hash) of the gateway must be also present within the list. The list can contain multiple hash values in array for multiple servers. +* +* When SDK fails to validate SSL with certificate or subjectKeyHash pinning for communication to HTTPs, SDK will cancel the request. +* +* If endPointPath is full URL format, upon successful SSL pinning validation, SDK will also validate the user session against primary gateway regardless the request is being made +* to the primary gateway or not. To ensure bypass the user session validation for public API, use [MAS deleteFrom:withParameters:requestType:responseType:isPublic:completion:] method +* with isPublic being YES. +* +* @param request MASRequest An object containing all parameters to call the endpoint +* When the value is set to true, all automatically injected credentials in SDK will be excluded in the request. +* @param dataTask MASDataTaskBlock A block which gives a MASDataTask object that can be used to cancel the request +* @param completion An MASResponseObjectErrorBlock (NSHTTPURLResponse *response, id responseObject, NSError *error) that will +* receive the NSHTTPURLResponse object, response object which needs to perform type casting based on the object type, and NSError object when error occurs. + @see cancelRequest +*/ + ++ (void)invoke:(nonnull MASRequest *)request taskBlock:(nullable MASDataTaskBlock)taskBlock completion:(nullable MASResponseObjectErrorBlock)completion; + + + # pragma mark - FILE Requests @@ -1088,6 +1114,62 @@ withParameters:(NSDictionary *_Nullable)parameterInfo */ + (void)postMultiPartForm:(nonnull MASRequest *)request constructingBodyWithBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:( MASFileRequestProgressBlock _Nullable )progressBlock completion:(nullable MASResponseObjectErrorBlock)completion; + +/** +* Post a multi-part form with the parameters defined in the MASRequest object +* +* If endPointPath is full URL format (including port number and http protocol), SDK will validate the server from the client side through SSL pinning (authentication challenge) with +* provided subjectKeyHash (also known as public key hash) in configuration in mag.mobile_sdk.trusted_cert_pinned_public_key_hashes and mag.mobile_sdk.enable_public_key_pinning. +* ALL of servers' public key hashes in certificate chain must be defined in the list. This means when it is configured to use public key hash pinning for SSL pinning, +* subjectKeyHash (public key hash) of the gateway must be also present within the list. The list can contain multiple hash values in array for multiple servers. +* +* When SDK fails to validate SSL with certificate or subjectKeyHash pinning for communication to HTTPs, SDK will cancel the request. +* +* If endPointPath is full URL format, upon successful SSL pinning validation, SDK will also validate the user session against primary gateway regardless the request is being made +* to the primary gateway or not. To ensure bypass the user session validation for public API, use [MAS deleteFrom:withParameters:requestType:responseType:isPublic:completion:] method +* with isPublic being YES. +* +* The API appends the file data and any other parameters to the body and follows the standards defined for Content-Type = multipart/form-data +* +* @param request MASRequest An object containing all parameters to call the endpoint +* When the value is set to true, all automatically injected credentials in SDK will be excluded in the request. +* @param formDataBlock This is a block that gets called while constructing the body of the multi-part form request. Typically files can be added to the body by calling various APIs available in MASMultiPartFormData +* @see MASMultiPartFormData +* @param progressBlock Block which gives back the NSProgress of the task that is going on. Different values can be read from the progress object to present a meaningful progress updates in the UI. Recommend to use progress.fractioncompleted for progress bar updates. +* @param taskBlock Block which gives back handle to the underlying task object. This task can be cancelled by calling cancel on the object. +* @param completion An MASResponseObjectErrorBlock (NSHTTPURLResponse *response, id responseObject, NSError *error) that will +* receive the NSHTTPURLResponse object, response object which needs to perform type casting based on the object type, and NSError object when error occurs. + @see cancelRequest +*/ ++ (void)postMultiPartForm:(nonnull MASRequest *)request constructingBodyWithBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:( MASFileRequestProgressBlock _Nullable )progressBlock taskBlock:(MASDataTaskBlock _Nullable )taskBlock completion:(nullable MASResponseObjectErrorBlock)completion; + + +///-------------------------------------- +/// @name Cancel HTTP Requests +///-------------------------------------- +# pragma mark - Cancel APIs + +/** +* API to cancel a pending HTTP request that was invoked using the API invoke:taksBlock:completion: +* This is a best effort cancel and uses the underlying cancel functionality of iOS. +* This means there could be scenarios where the request has already been complete and the response is received. In such cases calling cancel has no real meaning. +* The completion handler returns with an error description that the task has been cancelled. But in some cases if the request has a dependent Task like a login to be completed, then calling cancel on the request may not return a response back as the task has not been started yet. +* Cancelling an already finished task has no effect. +* @param task MASDataTask object that was obtained as a result of invoking a request using the API invoke:taksBlock:completion: +*/ + ++ (void)cancelRequest:(nonnull MASDataTask*)task error:(NSError*_Nullable*_Nullable)error; + + +/** +* API to cancel all the pending requests in the queue. +* This is a best effort cancel and uses the underlying cancel functionality of iOS. +* This means there could be scenarios where the request has already been complete and the response is received. In such cases calling cancel has no real meaning. +* All the taks that are pending and not finished yet will be cancelled. There is no guarantee for a callback with error for all the requests. + * Use this when you are sure to cancel all the existing requests and do not really care about the outcome. For example, User moved away from the flow and does not really need any response from earlier tasks. +*/ ++ (void)cancelAllRequests; + ///-------------------------------------- /// @name JWT Signing ///-------------------------------------- diff --git a/MASFoundation/Classes/MAS.m b/MASFoundation/Classes/MAS.m index 0521c675..586be88a 100644 --- a/MASFoundation/Classes/MAS.m +++ b/MASFoundation/Classes/MAS.m @@ -1139,6 +1139,34 @@ + (void)invoke:(nonnull MASRequest *)request completion:(nullable MASResponseObj } ++ (void)invoke:(nonnull MASRequest *)request taskBlock:(nullable MASDataTaskBlock)taskBlock completion:(nullable MASResponseObjectErrorBlock)completion +{ + __block MASResponseObjectErrorBlock blockCompletion = completion; + + + + [MAS checkAndValidateRequestScope:request.endPoint headerInfo:request.header isPublic:request.isPublic completion:^(BOOL completed, NSError *error) { + + if(!completed){ + completion(nil,nil,error); + return; + } + + // If default timeoutInterval override to NetworkConfiguration timeoutInterval. + NSTimeInterval timeoutInterval = + (request.timeoutInterval == MASDefaultNetworkTimeoutConfiguration) ? + [self timeoutIntervalForEndpoint:request.endPoint] : request.timeoutInterval; + + [MAS httpRouterMethod:request taskBlock:taskBlock completion:^(NSDictionary * _Nullable responseInfo, NSError * _Nullable error) { + if (blockCompletion) + { + blockCompletion([responseInfo objectForKey:MASNSHTTPURLResponseObjectKey], [responseInfo objectForKey:MASResponseInfoBodyInfoKey], error); + } + }]; + + }]; +} + + (void)postMultiPartForm:(nonnull MASRequest *)request constructingBodyWithBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:( MASFileRequestProgressBlock _Nullable )progressBlock completion:(nullable MASResponseObjectErrorBlock)completion { if(![request.httpMethod isEqualToString:@"POST"] || request.requestType != MASRequestResponseTypeFormData) @@ -1160,12 +1188,61 @@ + (void)postMultiPartForm:(nonnull MASRequest *)request constructingBodyWithBloc (request.timeoutInterval == MASDefaultNetworkTimeoutConfiguration) ? [self timeoutIntervalForEndpoint:request.endPoint] : request.timeoutInterval; - [[MASNetworkingService sharedService] postMultiPartForm:request.endPoint withParameters:request.body andHeaders:request.header requestType:request.requestType responseType:request.responseType isPublic:request.isPublic timeoutInterval:timeoutInterval constructingBodyBlock:formDataBlock progress:progressBlock completion:completion]; + + + [[MASNetworkingService sharedService] postMultiPartForm:request.endPoint withParameters:request.body andHeaders:request.header requestType:request.requestType responseType:request.responseType isPublic:request.isPublic timeoutInterval:timeoutInterval constructingBodyBlock:formDataBlock progress:progressBlock taskBlock:nil completion:completion]; + + }]; +} + ++ (void)postMultiPartForm:(nonnull MASRequest *)request constructingBodyWithBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:( MASFileRequestProgressBlock _Nullable )progressBlock taskBlock:(MASDataTaskBlock _Nullable )taskBlock completion:(nullable MASResponseObjectErrorBlock)completion +{ + if(![request.httpMethod isEqualToString:@"POST"] || request.requestType != MASRequestResponseTypeFormData) + { + NSError* error = [NSError errorInvalidRequestForFileUpload]; + completion(nil,nil,error); + return; + } + + [MAS checkAndValidateRequestScope:request.endPoint headerInfo:request.header isPublic:request.isPublic completion:^(BOOL completed, NSError *error) { + + if(!completed){ + completion(nil,nil,error); + return; + } + + // If default timeoutInterval override to NetworkConfiguration timeoutInterval. + NSTimeInterval timeoutInterval = + (request.timeoutInterval == MASDefaultNetworkTimeoutConfiguration) ? + [self timeoutIntervalForEndpoint:request.endPoint] : request.timeoutInterval; + + [[MASNetworkingService sharedService] postMultiPartForm:request.endPoint withParameters:request.body andHeaders:request.header requestType:request.requestType responseType:request.responseType isPublic:request.isPublic timeoutInterval:timeoutInterval constructingBodyBlock:formDataBlock progress:progressBlock taskBlock:taskBlock completion:completion]; }]; } ++ (void)cancelRequest:(nonnull MASDataTask*)task error:(NSError**)error +{ + // + // Check if MAS has been started. + // + if ([MAS MASState] != MASStateDidStart) + { + *error = [NSError errorMASIsNotStarted]; + return; + + } + + [[MASNetworkingService sharedService] cancelRequest:task error:error]; +} + ++ (void)cancelAllRequests +{ + [[MASNetworkingService sharedService] cancelAllRequests]; +} + + # pragma mark - Private + (void)checkAndValidateRequestScope:(NSString*)endPoint headerInfo:(NSDictionary *)headerInfo isPublic:(BOOL)isPublic completion:(MASCompletionErrorBlock)completion @@ -1317,6 +1394,53 @@ + (void)httpMethod:(NSString *)httpMethod }]; } + ++ (void)httpRouterMethod:(MASRequest*)request taskBlock:(_Nullable MASDataTaskBlock)taskBlock completion:(MASResponseInfoErrorBlock)completion +{ + // + // Check for endpoint + // + if (!request.endPoint) + { + if (completion) + { + completion(nil, [NSError errorInvalidEndpoint]); + + return; + } + } + + // + // Check if MAS has been started. + // + if ([MAS MASState] != MASStateDidStart) + { + if (completion) + { + completion(nil, [NSError errorMASIsNotStarted]); + + return; + } + } + + // + // Validate if new scope has been requested in header + // Validation will be ignored if the request is being made as public + // + NSDictionary *blockHeaderInfo = request.header ? request.header : [NSDictionary dictionary]; + + [MAS validateScopeForRequest:blockHeaderInfo isPublic:request.isPublic completion:^(BOOL completed, NSError *error) { + + if(!completed && error) + { + completion(nil,error); + return; + } + + [[MASNetworkingService sharedService] httpRequestWithCancel:request taskBlock:taskBlock completion:completion]; + }]; +} + + (MASResponseInfoErrorBlock)parseToEjectURLResponseForCompletionBlock:(MASResponseInfoErrorBlock)completionBlock { MASResponseInfoErrorBlock responseCompletionBlock = ^(NSDictionary *responseInfo, NSError *error) { diff --git a/MASFoundation/Classes/MASConstants.h b/MASFoundation/Classes/MASConstants.h index 1dcec58f..4e1db729 100644 --- a/MASFoundation/Classes/MASConstants.h +++ b/MASFoundation/Classes/MASConstants.h @@ -13,6 +13,7 @@ @class CLLocation; @class MASAuthCredentials; @class MASUser; +@class MASDataTask; @protocol MASMultiPartFormData; @@ -109,6 +110,11 @@ typedef void (^MASResponseInfoErrorBlock)(NSDictionary *_Nullabl typedef void (^MASResponseObjectErrorBlock)(NSHTTPURLResponse *_Nullable response, id _Nullable responseObject, NSError *_Nullable error); +/** +* The MASDataTaskBlock which contains the dataTask object that is being invoked. Users can typically call cancel on the request using this callback. +*/ +typedef void (^MASDataTaskBlock)(MASDataTask* _Nullable dataTask); + /** * The MASUser specific (MASUser *user, NSError *error) block. */ diff --git a/MASFoundation/Classes/MASDataTask.h b/MASFoundation/Classes/MASDataTask.h new file mode 100644 index 00000000..0cb29d27 --- /dev/null +++ b/MASFoundation/Classes/MASDataTask.h @@ -0,0 +1,25 @@ +// +// MASDataTask.h +// MASFoundation +// +// Copyright © 2019 CA Technologies. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MASDataTask : NSObject +{ + +} + +@property(readonly)NSString* taskID; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/MASFoundation/Classes/MASDataTask.m b/MASFoundation/Classes/MASDataTask.m new file mode 100644 index 00000000..9d5ea96e --- /dev/null +++ b/MASFoundation/Classes/MASDataTask.m @@ -0,0 +1,34 @@ +// +// MASDataTask.m +// MASFoundation +// +// Copyright © 2019 CA Technologies. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +#import "MASDataTask.h" +#import "MASSessionDataTaskOperation.h" + +@interface MASDataTask() +{ + +} +@property(nonatomic,readwrite,weak)MASSessionDataTaskOperation* operation; +@property(readwrite)NSString* taskID; +@end + + +@implementation MASDataTask + +- (id)init +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"Cannot create instances of this class, Object can only be used from taskBlock" userInfo:nil]; + + return nil; +} + + +@end diff --git a/MASFoundation/Classes/MASError.h b/MASFoundation/Classes/MASError.h index ea6bfe60..c1894a37 100644 --- a/MASFoundation/Classes/MASError.h +++ b/MASFoundation/Classes/MASError.h @@ -208,6 +208,21 @@ typedef NS_ENUM(NSInteger, MASFoundationErrorCode) // MASFoundationErrorCodeInvalidRequestForFileUpload = 180100, + // + // Data Task Cancelled + // + MASFoundationErrorCodeTaskCancelled = 180102, + + // + // Data task not found + // + MASFoundationErrorCodeDataTaskNotFound = 180103, + + // + // Data task not cancellable + // + MASFoundationErrorCodeDataTaskNotCancellable = 180104, + MASFoundationErrorCodeCount = -999999 }; diff --git a/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.h b/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.h index dc59163f..7942f72b 100644 --- a/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.h +++ b/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.h @@ -656,4 +656,31 @@ */ + (NSError *)errorInvalidRequestForFileUpload; + +/** +* Create MASFoundationErrorDomainLocal NSError for MASFoundationErrorCodeTaskCancelled. +* +* @return Returns an NSError instance with the domain MASFoundationErrorDomainLocal and +* error MASFoundationErrorCodeTaskCancelled +*/ ++ (NSError *)errorDataTaskCancelled; + +/** +* Create MASFoundationErrorDomainLocal NSError for MASFoundationDataTaskNotFound. +* +* @return Returns an NSError instance with the domain MASFoundationErrorDomainLocal and +* error MASFoundationDataTaskNotFound +*/ ++ (NSError *)errorDataTaskNotFound; + + +/** +* Create MASFoundationErrorDomainLocal NSError for MASFoundationDataTaskNotFound. +* +* @return Returns an NSError instance with the domain MASFoundationErrorDomainLocal and +* error MASFoundationDataTaskNotFound +*/ ++ (NSError *)errorDataTaskNotCancellable; + + @end diff --git a/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.m b/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.m index 173bdbcc..de9658f8 100644 --- a/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.m +++ b/MASFoundation/Classes/_private_/categories/NSError+MASPrivate.m @@ -838,6 +838,21 @@ + (NSError *)errorInvalidRequestForFileUpload return [self errorForFoundationCode:MASFoundationErrorCodeInvalidRequestForFileUpload errorDomain:MASFoundationErrorDomainLocal]; } ++ (NSError *)errorDataTaskCancelled +{ + return [self errorForFoundationCode:MASFoundationErrorCodeTaskCancelled errorDomain:MASFoundationErrorDomainLocal]; +} + ++ (NSError *)errorDataTaskNotFound +{ + return [self errorForFoundationCode:MASFoundationErrorCodeDataTaskNotFound errorDomain:MASFoundationErrorDomainLocal]; +} + ++ (NSError *)errorDataTaskNotCancellable +{ + return [self errorForFoundationCode:MASFoundationErrorCodeDataTaskNotCancellable errorDomain:MASFoundationErrorDomainLocal]; +} + # pragma mark - Foundation Errors Private @@ -1114,6 +1129,25 @@ + (NSString *)descriptionForFoundationErrorCode:(MASFoundationErrorCode)errorCod case MASFoundationErrorCodeInvalidRequestForFileUpload : return @"the MASRequest is not valid for multi-part file upload. Please check if the request is of method POST and the request type is MASRequestResponseTypeFormData."; + // + // Task Cancelled Error + // + case MASFoundationErrorCodeTaskCancelled : return @"The request has been Cancelled"; + + + // + // Task Not found Error + // + + case MASFoundationErrorCodeDataTaskNotFound : return @"The given DataTask is either invalid or does not exist"; + + + // + // Task Can not be Cancelled Error + // + + case MASFoundationErrorCodeDataTaskNotCancellable : return @"Unable to cancel the task. The Task is either finished or cancelled"; + // // Default // diff --git a/MASFoundation/Classes/_private_/services/network/MASNetworkingService.h b/MASFoundation/Classes/_private_/services/network/MASNetworkingService.h index ecca1591..bc70d5c3 100644 --- a/MASFoundation/Classes/_private_/services/network/MASNetworkingService.h +++ b/MASFoundation/Classes/_private_/services/network/MASNetworkingService.h @@ -847,9 +847,15 @@ timeoutInterval:(NSTimeInterval)timeoutInterval +- (void)httpRequestWithCancel:(MASRequest*)request taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseInfoErrorBlock)completion; + +- (void)cancelRequest:(MASDataTask*)task error:(NSError**)error; + +- (void)cancelAllRequests; + # pragma mark - HTTP File Requests -- (void)postMultiPartForm:(NSString*)endPoint withParameters:(NSDictionary *)parameterInfo andHeaders:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress completion:(MASResponseObjectErrorBlock)completion; +- (void)postMultiPartForm:(NSString*)endPoint withParameters:(NSDictionary *)parameterInfo andHeaders:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseObjectErrorBlock)completion; @end diff --git a/MASFoundation/Classes/_private_/services/network/MASNetworkingService.m b/MASFoundation/Classes/_private_/services/network/MASNetworkingService.m index d3e6ffc8..15bc8eee 100644 --- a/MASFoundation/Classes/_private_/services/network/MASNetworkingService.m +++ b/MASFoundation/Classes/_private_/services/network/MASNetworkingService.m @@ -30,7 +30,7 @@ #import "MASNetworkReachability.h" #import "MASMultiFactorHandler+MASPrivate.h" #import "MASMultiPartRequestSerializer.h" - +#import "MASDataTask+MASPrivate.h" # pragma mark - Configuration Constants @@ -63,6 +63,7 @@ @interface MASNetworkingService () @property (nonatomic, strong, readwrite) MASURLSessionManager *sessionManager; @property (nonatomic, strong, readwrite) MASNetworkReachability *gatewayReachabilityManager; @property (readwrite, nonatomic, strong) MASAuthValidationOperation *authValidationOperation; +@property (nonatomic, strong) NSMutableDictionary* tasks; @end @@ -240,8 +241,10 @@ + (instancetype)sharedService dispatch_once(&onceToken, ^ { sharedInstance = [[MASNetworkingService alloc] initProtected]; + }); + return sharedInstance; } @@ -269,6 +272,10 @@ - (void)serviceDidLoad - (void)serviceWillStart { + + if(!self.tasks){ + self.tasks = [[NSMutableDictionary alloc] init]; + } // // establish URLSession with configuration's host name and start networking monitoring // @@ -289,6 +296,7 @@ - (void)serviceWillStop [_gatewayReachabilityManager stopMonitoring]; _sessionManager = nil; } + [self cleanUpFinishedTasks]; [super serviceWillStop]; } @@ -431,8 +439,7 @@ - (MASSessionDataTaskCompletionBlock)sessionDataTaskCompletionBlockWithEndPoint: httpMethod:(NSString *)httpMethod requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType - isPublic:(BOOL)isPublic - completionBlock:(MASResponseInfoErrorBlock)completion + isPublic:(BOOL)isPublic completionBlock:(MASResponseInfoErrorBlock)completion { __block MASRequestResponseType blockResponseType = responseType; __block MASRequestResponseType blockRequestType = requestType; @@ -461,6 +468,7 @@ - (MASSessionDataTaskCompletionBlock)sessionDataTaskCompletionBlockWithEndPoint: // Response header info // NSDictionary *headerInfo = [httpResponse allHeaderFields]; + NSLog(@"Response headers : %@",headerInfo); // // If the error exists from the server, inject http status code in error userInfo @@ -1409,6 +1417,24 @@ - (void)httpPostTo:(NSString *)endPoint [self httpRequest:@"POST" endPoint:endPoint parameters:parameterInfo headers:headerInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval completion:completion]; } +- (void)httpPostTo:(MASRequest*)request taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseInfoErrorBlock)completion +{ + // + // endPoint cannot be nil + // + if (!request.endPoint) + { + // + // Notify + // + if(completion) completion(nil, [NSError errorInvalidEndpoint]); + + return; + } + + +} + - (void)putTo:(NSString *)endPoint withParameters:(NSDictionary *)parameterInfo @@ -1519,7 +1545,7 @@ - (void)httpPutTo:(NSString *)endPoint } -- (void)postMultiPartForm:(NSString*)endPoint withParameters:(NSDictionary *)parameterInfo andHeaders:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress completion:(MASResponseObjectErrorBlock)completion +- (void)postMultiPartForm:(NSString*)endPoint withParameters:(NSDictionary *)parameterInfo andHeaders:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseObjectErrorBlock)completion { // // endPoint cannot be nil @@ -1534,11 +1560,11 @@ - (void)postMultiPartForm:(NSString*)endPoint withParameters:(NSDictionary *)par return; } - [self httpFileUploadRequest:endPoint parameters:parameterInfo headers:headerInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval constructingBodyBlock:formDataBlock progress:progress completion:completion]; + [self httpFileUploadRequest:endPoint parameters:parameterInfo headers:headerInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval constructingBodyBlock:formDataBlock progress:progress taskBlock:taskBlock completion:completion]; } -- (void)httpFileUploadRequest:(NSString *)endPoint parameters:(NSDictionary *)parameterInfo headers:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress completion:(MASResponseObjectErrorBlock)completion +- (void)httpFileUploadRequest:(NSString *)endPoint parameters:(NSDictionary *)parameterInfo headers:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval constructingBodyBlock:(nonnull MASMultiPartFormDataBlock)formDataBlock progress:(MASFileRequestProgressBlock)progress taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseObjectErrorBlock)completion { NSMutableDictionary *mutableHeaderInfo = [headerInfo mutableCopy]; @@ -1569,6 +1595,10 @@ - (void)httpFileUploadRequest:(NSString *)endPoint parameters:(NSDictionary *)pa } }]]; + MASDataTask* newDataTask = [[MASDataTask alloc] initWithTask:operation]; + [self cacheDataTask:newDataTask]; + + if (![self isMAGEndpoint:endPoint]) { // @@ -1606,9 +1636,14 @@ - (void)httpFileUploadRequest:(NSString *)endPoint parameters:(NSDictionary *)pa // [_sessionManager.internalOperationQueue addOperation:operation]; } + + if(taskBlock){ + taskBlock(newDataTask); + } } + - (void)httpRequest:(NSString *)httpMethod endPoint:(NSString *)endPoint parameters:(NSDictionary *)parameterInfo headers:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic timeoutInterval:(NSTimeInterval)timeoutInterval completion:(MASResponseInfoErrorBlock)completion { @@ -1709,4 +1744,192 @@ - (void)httpRequest:(NSString *)httpMethod endPoint:(NSString *)endPoint paramet } } + +- (void)httpRequestWithCancel:(MASRequest*)request taskBlock:(MASDataTaskBlock)taskBlock completion:(MASResponseInfoErrorBlock)completion +{ + NSMutableDictionary *mutableHeaderInfo; + // + // Update the header + // + if(request.header){ + mutableHeaderInfo = [request.header mutableCopy]; + } + else{ + mutableHeaderInfo = [NSMutableDictionary mutableCopy]; + } + + + MASURLRequest* urlRequest = [self getURLRequest:request.httpMethod endPoint:request.endPoint parameters:request.body headers:[NSDictionary dictionary] requestType:request.requestType responseType:request.responseType isPublic:request.isPublic timeoutInterval:request.timeoutInterval]; + + // + // if location was successfully retrieved + // + if ([MASLocationService sharedService].lastKnownLocation != nil) + { + mutableHeaderInfo[MASGeoLocationRequestResponseKey] = [[MASLocationService sharedService].lastKnownLocation locationAsGeoCoordinates]; + } + + if(self.httpRedirectionBlock) + { + [_sessionManager setSessionDidReceiveHTTPRedirectBlock:self.httpRedirectionBlock]; + } + + MASSessionDataTaskOperation *operation = [_sessionManager dataOperationWithRequest:urlRequest + completionHandler:[self sessionDataTaskCompletionBlockWithEndPoint:request.endPoint + parameters:request.body + headers:request.header + httpMethod:urlRequest.HTTPMethod + requestType:request.requestType + responseType:request.responseType + isPublic:request.isPublic completionBlock:completion]]; + + + MASDataTask* newDataTask = [[MASDataTask alloc] initWithTask:operation]; + DLog(@"MASNetworkingService : created Task with ID %@",newDataTask.taskID); + [self cacheDataTask:newDataTask]; + + + + if (![self isMAGEndpoint:request.endPoint]) + { + // + // if the request is being made to system endpoint, and is not a public request which requires user credentials (tokens) + // then, add dependency on shared validation operation which will validate current session + // sharedOperation will only exist one at any given time as long as sharedOperation is being executed + // + if (!request.isPublic) + { + // + // add dependency + // + [operation addDependency:self.sharedOperation]; + + // + // to make sure SDK to not enqueue sharedOperation that is already enqueue and being executed + // + if (!self.sharedOperation.isFinished && !self.sharedOperation.isExecuting && ![_sessionManager.internalOperationQueue.operations containsObject:self.sharedOperation]) + { + // + // add sharedOperation into internal operation queue + // + [_sessionManager.internalOperationQueue addOperation:self.sharedOperation]; + } + } + + // + // add current request into normal operation queue + // + [_sessionManager.operationQueue addOperation:operation]; + } + else { + // + // if the request is being made to any one of system endpoints (registration, and/or authentication), then, add the operation into internal operation queue + // + [_sessionManager.internalOperationQueue addOperation:operation]; + } + + if(taskBlock){ + taskBlock(newDataTask); + } + +} + + +- (void)cacheDataTask:(MASDataTask*)dataTask +{ + //[self cleanUpFinishedTasks]; + if(dataTask.taskID){ + DLog(@"MASNetworkingService : Added Task with ID %@ to the cache",dataTask.taskID); + [self.tasks setObject:dataTask forKey:dataTask.taskID]; + } +} + +- (void)cleanUpFinishedTasks +{ + NSLog(@"cleanUpFinishedTasks : cleaning up tasks"); + NSMutableArray* keysToRemove = [[NSMutableArray alloc] init]; + for (NSString* key in self.tasks){ + if([[self.tasks objectForKey:key] isFinished] || [[self.tasks objectForKey:key] isCancelled]){ + [keysToRemove addObject:key]; + //[self.tasks removeObjectForKey:key]; + } + } + [self.tasks removeObjectsForKeys:keysToRemove]; + NSLog(@"cleanUpFinishedTasks : finished cleaning up"); +} + +- (void)cancelRequest:(MASDataTask*)task error:(NSError**)error; +{ + NSString* taskID = task.taskID; + if(self.tasks && [self.tasks objectForKey:taskID]){ + MASDataTask* taskToBeCancelled = [self.tasks objectForKey:taskID]; + BOOL isTaskCancelled = [taskToBeCancelled cancelTask]; + [self.tasks removeObjectForKey:taskToBeCancelled.taskID]; + + if (!isTaskCancelled){ + *error = [NSError errorDataTaskNotFound]; + } + } + else { + //task not found error + *error = [NSError errorDataTaskNotFound]; + } + +} + + +- (void)cancelAllRequests +{ + NSLog(@"Cancel All Requests"); + [[_sessionManager operationQueue] cancelAllOperations]; + [[_sessionManager internalOperationQueue] cancelAllOperations]; +} + + + +- (MASURLRequest*)getURLRequest:(NSString *)httpMethod endPoint:(NSString *)endPoint parameters:(NSDictionary *)parameterInfo headers:(NSDictionary *)headerInfo requestType:(MASRequestResponseType)requestType responseType:(MASRequestResponseType)responseType isPublic:(BOOL)isPublic +timeoutInterval:(NSTimeInterval)timeoutInterval +{ + // + // Update the header + // + NSMutableDictionary *mutableHeaderInfo = [headerInfo mutableCopy]; + + MASURLRequest *request = nil; + + // + // if location was successfully retrieved + // + if ([MASLocationService sharedService].lastKnownLocation != nil) + { + mutableHeaderInfo[MASGeoLocationRequestResponseKey] = [[MASLocationService sharedService].lastKnownLocation locationAsGeoCoordinates]; + } + + // + // Construct MASURLRequest object per HTTP method + // + if ([httpMethod isEqualToString:@"DELETE"]) + { + request = [MASDeleteURLRequest requestForEndpoint:request.endPoint withParameters:parameterInfo andHeaders:mutableHeaderInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval]; + } + else if ([httpMethod isEqualToString:@"GET"]) + { + request = [MASGetURLRequest requestForEndpoint:endPoint withParameters:parameterInfo andHeaders:mutableHeaderInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval]; + } + else if ([httpMethod isEqualToString:@"PATCH"]) + { + request = [MASPatchURLRequest requestForEndpoint:endPoint withParameters:parameterInfo andHeaders:mutableHeaderInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval]; + } + else if ([httpMethod isEqualToString:@"POST"]) + { + request = [MASPostURLRequest requestForEndpoint:endPoint withParameters:parameterInfo andHeaders:mutableHeaderInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval]; + } + else if ([httpMethod isEqualToString:@"PUT"]) + { + request = [MASPutURLRequest requestForEndpoint:endPoint withParameters:parameterInfo andHeaders:mutableHeaderInfo requestType:requestType responseType:responseType isPublic:isPublic timeoutInterval:timeoutInterval]; + } + + return request; +} + @end diff --git a/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.h b/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.h new file mode 100644 index 00000000..517c15c3 --- /dev/null +++ b/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.h @@ -0,0 +1,29 @@ +// +// MASDataTask+MASPrivate.h +// MASFoundation +// +// Copyright © 2019 CA Technologies. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +#import +#import "MASDataTask.h" +#import "MASSessionDataTaskOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MASDataTask (MASPrivate) +{ + +} + +- (instancetype)initWithTask:(MASSessionDataTaskOperation*)operation; +- (BOOL)isFinished; +-(BOOL)isCancelled; +- (BOOL)cancelTask; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.m b/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.m new file mode 100644 index 00000000..32c15a65 --- /dev/null +++ b/MASFoundation/Classes/_private_/services/network/internal/MASDataTask+MASPrivate.m @@ -0,0 +1,62 @@ +// +// MASDataTask+MASPrivate.m +// MASFoundation +// +// Copyright © 2019 CA Technologies. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +#import "MASDataTask+MASPrivate.h" + +@interface MASDataTask() +{ + +} + +@property(readwrite)NSString* taskID; +@property(nonatomic,readwrite)MASSessionDataTaskOperation* operation; + +@end + +@implementation MASDataTask (MASPrivate) + + +- (instancetype)initWithTask:(MASSessionDataTaskOperation*)operation +{ + if(self = [super init]){ + self.operation = operation; + self.taskID = operation.taskID; + } + + return self; +} + +- (BOOL)isFinished +{ + return [self.operation isFinished]; +} + + +-(BOOL)isCancelled +{ + return [self.operation isCancelled]; +} + +- (BOOL)cancelTask +{ + if(self.operation && (![self.operation isFinished] && ![self.operation isCancelled])){ + DLog(@"Cancelling task with ID %@",self.taskID); + [self.operation cancel]; + return YES; + } + + DLog(@"Unable to cancel task. The operation is either finished or cancelled earlier"); + return NO; +} + + + + +@end diff --git a/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.h b/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.h index 37b832fc..9304ee12 100644 --- a/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.h +++ b/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.h @@ -22,6 +22,7 @@ @property (nonatomic, copy) MASNetworkDataTaskDidReceiveDataBlock didReceiveDataBlock; @property (nonatomic, copy) MASNetworkDataTaskWillCacheResponseBlock willCacheResponseBlock; @property (nonatomic, copy) MASNetworkDataTaskDidReceiveResponseBlock didReceiveResponseBlock; +@property (nonatomic,readonly) NSString* taskID; - (instancetype)initWithSession:(NSURLSession *)session request:(NSURLRequest *)request progress:(MASFileRequestProgressBlock)progress; diff --git a/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.m b/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.m index 7889c12a..1f758336 100644 --- a/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.m +++ b/MASFoundation/Classes/_private_/services/network/internal/MASSessionDataTaskOperation.m @@ -29,6 +29,7 @@ @interface MASSessionDataTaskOperation () @property (nonatomic, readwrite, strong) MASURLRequest *request; @property (nonatomic, readwrite, strong) NSURLSession *session; +@property (nonatomic, strong) NSString* taskID; @property (nonatomic)MASFileRequestProgressBlock fileProgressblock; @@ -49,6 +50,7 @@ - (instancetype)initWithSession:(NSURLSession *)session request:(NSURLRequest *) { self.request = (MASURLRequest *)request; [self setResponseType:self.request.responseType]; + self.taskID = [[NSUUID UUID] UUIDString]; } return self; @@ -62,6 +64,7 @@ - (instancetype)initWithSession:(NSURLSession *)session request:(NSURLRequest *) self.request = (MASURLRequest *)request; [self setResponseType:self.request.responseType]; self.fileProgressblock = progress; + self.taskID = [[NSUUID UUID] UUIDString]; } return self; @@ -300,7 +303,7 @@ - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { - NSLog(@"total bytes sent - %lld total bytes expected %lld",totalBytesSent,totalBytesExpectedToSend); + //DLog(@"total bytes sent - %lld total bytes expected %lld",totalBytesSent,totalBytesExpectedToSend); if(self.fileProgressblock){ //NSProgress* progress = [NSProgress progressWithTotalUnitCount:totalBytesExpectedToSend]; //[progress set] diff --git a/MASFoundation/Classes/_private_/services/network/internal/MASSessionTaskOperation.m b/MASFoundation/Classes/_private_/services/network/internal/MASSessionTaskOperation.m index db685612..257e45dc 100644 --- a/MASFoundation/Classes/_private_/services/network/internal/MASSessionTaskOperation.m +++ b/MASFoundation/Classes/_private_/services/network/internal/MASSessionTaskOperation.m @@ -135,6 +135,16 @@ - (void)start - (void)cancel { + if(!self.isExecuting){ + [self.task cancel]; + [super cancel]; + if(self.didCompleteWithDataErrorBlock){ + self.didCompleteWithDataErrorBlock(nil, nil, nil, [NSError errorDataTaskCancelled]); + } + + return; + } + [self.task cancel]; [super cancel]; } diff --git a/MASFoundation/Classes/models/MASBrowserBasedAuthentication.m b/MASFoundation/Classes/models/MASBrowserBasedAuthentication.m index b63efe76..a10e97f2 100644 --- a/MASFoundation/Classes/models/MASBrowserBasedAuthentication.m +++ b/MASFoundation/Classes/models/MASBrowserBasedAuthentication.m @@ -197,7 +197,10 @@ - (MASSessionDataTaskHTTPRedirectBlock)getRedirectionBlock blockSelf.webLoginCallBack(nil, YES, nil); } } - return nil; + //return a nil request as we would have already cancelled the request + //return a nil as compiler expects a NSURLRequest object + NSURLRequest* nilRequest = nil; + return nilRequest; }; return redirectionBlock; diff --git a/MASFoundation/MASFoundation.h b/MASFoundation/MASFoundation.h index 7f36d17b..4f76e160 100644 --- a/MASFoundation/MASFoundation.h +++ b/MASFoundation/MASFoundation.h @@ -53,6 +53,7 @@ FOUNDATION_EXPORT const unsigned char MASFoundationVersionString[]; #import #import #import +#import // // AuthCredentials Models