AFURLSessionManager.m 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. // AFURLSessionManager.m
  2. // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. #import "AFURLSessionManager.h"
  22. #import <objc/runtime.h>
  23. static dispatch_queue_t url_session_manager_processing_queue() {
  24. static dispatch_queue_t af_url_session_manager_processing_queue;
  25. static dispatch_once_t onceToken;
  26. dispatch_once(&onceToken, ^{
  27. af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
  28. });
  29. return af_url_session_manager_processing_queue;
  30. }
  31. static dispatch_group_t url_session_manager_completion_group() {
  32. static dispatch_group_t af_url_session_manager_completion_group;
  33. static dispatch_once_t onceToken;
  34. dispatch_once(&onceToken, ^{
  35. af_url_session_manager_completion_group = dispatch_group_create();
  36. });
  37. return af_url_session_manager_completion_group;
  38. }
  39. NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
  40. NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
  41. NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
  42. NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
  43. NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification = @"com.alamofire.networking.session.download.file-manager-succeed";
  44. NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
  45. NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
  46. NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
  47. NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
  48. NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
  49. NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
  50. NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics = @"com.alamofire.networking.complete.sessiontaskmetrics";
  51. static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
  52. typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
  53. typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
  54. typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
  55. typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
  56. typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential));
  57. typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
  58. typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
  59. typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
  60. typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
  61. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  62. typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics) AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
  63. #endif
  64. typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
  65. typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
  66. typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
  67. typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);
  68. typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
  69. typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
  70. typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
  71. typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
  72. typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
  73. #pragma mark -
  74. @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
  75. - (instancetype)initWithTask:(NSURLSessionTask *)task;
  76. @property (nonatomic, weak) AFURLSessionManager *manager;
  77. @property (nonatomic, strong) NSMutableData *mutableData;
  78. @property (nonatomic, strong) NSProgress *uploadProgress;
  79. @property (nonatomic, strong) NSProgress *downloadProgress;
  80. @property (nonatomic, copy) NSURL *downloadFileURL;
  81. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  82. @property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
  83. #endif
  84. @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
  85. @property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
  86. @property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
  87. @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
  88. @end
  89. @implementation AFURLSessionManagerTaskDelegate
  90. - (instancetype)initWithTask:(NSURLSessionTask *)task {
  91. self = [super init];
  92. if (!self) {
  93. return nil;
  94. }
  95. _mutableData = [NSMutableData data];
  96. _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
  97. _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
  98. __weak __typeof__(task) weakTask = task;
  99. for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
  100. {
  101. progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
  102. progress.cancellable = YES;
  103. progress.cancellationHandler = ^{
  104. [weakTask cancel];
  105. };
  106. progress.pausable = YES;
  107. progress.pausingHandler = ^{
  108. [weakTask suspend];
  109. };
  110. #if AF_CAN_USE_AT_AVAILABLE
  111. if (@available(macOS 10.11, *))
  112. #else
  113. if ([progress respondsToSelector:@selector(setResumingHandler:)])
  114. #endif
  115. {
  116. progress.resumingHandler = ^{
  117. [weakTask resume];
  118. };
  119. }
  120. [progress addObserver:self
  121. forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
  122. options:NSKeyValueObservingOptionNew
  123. context:NULL];
  124. }
  125. return self;
  126. }
  127. - (void)dealloc {
  128. [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
  129. [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
  130. }
  131. #pragma mark - NSProgress Tracking
  132. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  133. if ([object isEqual:self.downloadProgress]) {
  134. if (self.downloadProgressBlock) {
  135. self.downloadProgressBlock(object);
  136. }
  137. }
  138. else if ([object isEqual:self.uploadProgress]) {
  139. if (self.uploadProgressBlock) {
  140. self.uploadProgressBlock(object);
  141. }
  142. }
  143. }
  144. static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey;
  145. #pragma mark - NSURLSessionTaskDelegate
  146. - (void)URLSession:(__unused NSURLSession *)session
  147. task:(NSURLSessionTask *)task
  148. didCompleteWithError:(NSError *)error
  149. {
  150. error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
  151. __strong AFURLSessionManager *manager = self.manager;
  152. __block id responseObject = nil;
  153. NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
  154. userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
  155. //Performance Improvement from #2672
  156. NSData *data = nil;
  157. if (self.mutableData) {
  158. data = [self.mutableData copy];
  159. //We no longer need the reference, so nil it out to gain back some memory.
  160. self.mutableData = nil;
  161. }
  162. #if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
  163. if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
  164. if (self.sessionTaskMetrics) {
  165. userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
  166. }
  167. }
  168. #endif
  169. if (self.downloadFileURL) {
  170. userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
  171. } else if (data) {
  172. userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
  173. }
  174. if (error) {
  175. userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
  176. dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
  177. if (self.completionHandler) {
  178. self.completionHandler(task.response, responseObject, error);
  179. }
  180. dispatch_async(dispatch_get_main_queue(), ^{
  181. [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
  182. });
  183. });
  184. } else {
  185. dispatch_async(url_session_manager_processing_queue(), ^{
  186. NSError *serializationError = nil;
  187. responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
  188. if (self.downloadFileURL) {
  189. responseObject = self.downloadFileURL;
  190. }
  191. if (responseObject) {
  192. userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
  193. }
  194. if (serializationError) {
  195. userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
  196. }
  197. dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
  198. if (self.completionHandler) {
  199. self.completionHandler(task.response, responseObject, serializationError);
  200. }
  201. dispatch_async(dispatch_get_main_queue(), ^{
  202. [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
  203. });
  204. });
  205. });
  206. }
  207. }
  208. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  209. - (void)URLSession:(NSURLSession *)session
  210. task:(NSURLSessionTask *)task
  211. didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
  212. self.sessionTaskMetrics = metrics;
  213. }
  214. #endif
  215. #pragma mark - NSURLSessionDataDelegate
  216. - (void)URLSession:(__unused NSURLSession *)session
  217. dataTask:(__unused NSURLSessionDataTask *)dataTask
  218. didReceiveData:(NSData *)data
  219. {
  220. self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
  221. self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
  222. [self.mutableData appendData:data];
  223. }
  224. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
  225. didSendBodyData:(int64_t)bytesSent
  226. totalBytesSent:(int64_t)totalBytesSent
  227. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  228. self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
  229. self.uploadProgress.completedUnitCount = task.countOfBytesSent;
  230. }
  231. #pragma mark - NSURLSessionDownloadDelegate
  232. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
  233. didWriteData:(int64_t)bytesWritten
  234. totalBytesWritten:(int64_t)totalBytesWritten
  235. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
  236. self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
  237. self.downloadProgress.completedUnitCount = totalBytesWritten;
  238. }
  239. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
  240. didResumeAtOffset:(int64_t)fileOffset
  241. expectedTotalBytes:(int64_t)expectedTotalBytes{
  242. self.downloadProgress.totalUnitCount = expectedTotalBytes;
  243. self.downloadProgress.completedUnitCount = fileOffset;
  244. }
  245. - (void)URLSession:(NSURLSession *)session
  246. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  247. didFinishDownloadingToURL:(NSURL *)location
  248. {
  249. self.downloadFileURL = nil;
  250. if (self.downloadTaskDidFinishDownloading) {
  251. self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
  252. if (self.downloadFileURL) {
  253. NSError *fileManagerError = nil;
  254. if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
  255. [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
  256. } else {
  257. [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
  258. }
  259. }
  260. }
  261. }
  262. @end
  263. #pragma mark -
  264. /**
  265. * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`.
  266. *
  267. * See:
  268. * - https://github.com/AFNetworking/AFNetworking/issues/1477
  269. * - https://github.com/AFNetworking/AFNetworking/issues/2638
  270. * - https://github.com/AFNetworking/AFNetworking/pull/2702
  271. */
  272. static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
  273. Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
  274. Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
  275. method_exchangeImplementations(originalMethod, swizzledMethod);
  276. }
  277. static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
  278. return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
  279. }
  280. static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume";
  281. static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
  282. @interface _AFURLSessionTaskSwizzling : NSObject
  283. @end
  284. @implementation _AFURLSessionTaskSwizzling
  285. + (void)load {
  286. /**
  287. WARNING: Trouble Ahead
  288. https://github.com/AFNetworking/AFNetworking/pull/2702
  289. */
  290. if (NSClassFromString(@"NSURLSessionTask")) {
  291. /**
  292. iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky.
  293. Many Unit Tests have been built to validate as much of this behavior has possible.
  294. Here is what we know:
  295. - 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.
  296. - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there.
  297. - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`.
  298. - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`.
  299. - 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.
  300. - 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.
  301. - 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.
  302. Some Assumptions:
  303. - 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.
  304. - No background task classes override `resume` or `suspend`
  305. The current solution:
  306. 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
  307. 2) Grab a pointer to the original implementation of `af_resume`
  308. 3) Check to see if the current class has an implementation of resume. If so, continue to step 4.
  309. 4) Grab the super class of the current class.
  310. 5) Grab a pointer for the current class to the current implementation of `resume`.
  311. 6) Grab a pointer for the super class to the current implementation of `resume`.
  312. 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
  313. 8) Set the current class to the super class, and repeat steps 3-8
  314. */
  315. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
  316. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  317. #pragma GCC diagnostic push
  318. #pragma GCC diagnostic ignored "-Wnonnull"
  319. NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
  320. #pragma clang diagnostic pop
  321. IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
  322. Class currentClass = [localDataTask class];
  323. while (class_getInstanceMethod(currentClass, @selector(resume))) {
  324. Class superClass = [currentClass superclass];
  325. IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
  326. IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
  327. if (classResumeIMP != superclassResumeIMP &&
  328. originalAFResumeIMP != classResumeIMP) {
  329. [self swizzleResumeAndSuspendMethodForClass:currentClass];
  330. }
  331. currentClass = [currentClass superclass];
  332. }
  333. [localDataTask cancel];
  334. [session finishTasksAndInvalidate];
  335. }
  336. }
  337. + (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
  338. Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
  339. Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
  340. if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
  341. af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
  342. }
  343. if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
  344. af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
  345. }
  346. }
  347. - (NSURLSessionTaskState)state {
  348. NSAssert(NO, @"State method should never be called in the actual dummy class");
  349. return NSURLSessionTaskStateCanceling;
  350. }
  351. - (void)af_resume {
  352. NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
  353. NSURLSessionTaskState state = [self state];
  354. [self af_resume];
  355. if (state != NSURLSessionTaskStateRunning) {
  356. [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
  357. }
  358. }
  359. - (void)af_suspend {
  360. NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
  361. NSURLSessionTaskState state = [self state];
  362. [self af_suspend];
  363. if (state != NSURLSessionTaskStateSuspended) {
  364. [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
  365. }
  366. }
  367. @end
  368. #pragma mark -
  369. @interface AFURLSessionManager ()
  370. @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
  371. @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
  372. @property (readwrite, nonatomic, strong) NSURLSession *session;
  373. @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
  374. @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
  375. @property (readwrite, nonatomic, strong) NSLock *lock;
  376. @property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
  377. @property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
  378. @property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
  379. @property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
  380. @property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
  381. @property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
  382. @property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
  383. @property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
  384. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  385. @property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
  386. #endif
  387. @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
  388. @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
  389. @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
  390. @property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
  391. @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
  392. @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
  393. @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
  394. @end
  395. @implementation AFURLSessionManager
  396. - (instancetype)init {
  397. return [self initWithSessionConfiguration:nil];
  398. }
  399. - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
  400. self = [super init];
  401. if (!self) {
  402. return nil;
  403. }
  404. if (!configuration) {
  405. configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  406. }
  407. self.sessionConfiguration = configuration;
  408. self.operationQueue = [[NSOperationQueue alloc] init];
  409. self.operationQueue.maxConcurrentOperationCount = 1;
  410. self.responseSerializer = [AFJSONResponseSerializer serializer];
  411. self.securityPolicy = [AFSecurityPolicy defaultPolicy];
  412. #if !TARGET_OS_WATCH
  413. self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
  414. #endif
  415. self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
  416. self.lock = [[NSLock alloc] init];
  417. self.lock.name = AFURLSessionManagerLockName;
  418. [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
  419. for (NSURLSessionDataTask *task in dataTasks) {
  420. [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
  421. }
  422. for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
  423. [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
  424. }
  425. for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
  426. [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
  427. }
  428. }];
  429. return self;
  430. }
  431. - (void)dealloc {
  432. [[NSNotificationCenter defaultCenter] removeObserver:self];
  433. }
  434. #pragma mark -
  435. - (NSURLSession *)session {
  436. @synchronized (self) {
  437. if (!_session) {
  438. _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
  439. }
  440. }
  441. return _session;
  442. }
  443. #pragma mark -
  444. - (NSString *)taskDescriptionForSessionTasks {
  445. return [NSString stringWithFormat:@"%p", self];
  446. }
  447. - (void)taskDidResume:(NSNotification *)notification {
  448. NSURLSessionTask *task = notification.object;
  449. if ([task respondsToSelector:@selector(taskDescription)]) {
  450. if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
  451. dispatch_async(dispatch_get_main_queue(), ^{
  452. [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
  453. });
  454. }
  455. }
  456. }
  457. - (void)taskDidSuspend:(NSNotification *)notification {
  458. NSURLSessionTask *task = notification.object;
  459. if ([task respondsToSelector:@selector(taskDescription)]) {
  460. if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
  461. dispatch_async(dispatch_get_main_queue(), ^{
  462. [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
  463. });
  464. }
  465. }
  466. }
  467. #pragma mark -
  468. - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
  469. NSParameterAssert(task);
  470. AFURLSessionManagerTaskDelegate *delegate = nil;
  471. [self.lock lock];
  472. delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
  473. [self.lock unlock];
  474. return delegate;
  475. }
  476. - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
  477. forTask:(NSURLSessionTask *)task
  478. {
  479. NSParameterAssert(task);
  480. NSParameterAssert(delegate);
  481. [self.lock lock];
  482. self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
  483. [self addNotificationObserverForTask:task];
  484. [self.lock unlock];
  485. }
  486. - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
  487. uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  488. downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  489. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  490. {
  491. AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
  492. delegate.manager = self;
  493. delegate.completionHandler = completionHandler;
  494. dataTask.taskDescription = self.taskDescriptionForSessionTasks;
  495. [self setDelegate:delegate forTask:dataTask];
  496. delegate.uploadProgressBlock = uploadProgressBlock;
  497. delegate.downloadProgressBlock = downloadProgressBlock;
  498. }
  499. - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
  500. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  501. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  502. {
  503. AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
  504. delegate.manager = self;
  505. delegate.completionHandler = completionHandler;
  506. uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
  507. [self setDelegate:delegate forTask:uploadTask];
  508. delegate.uploadProgressBlock = uploadProgressBlock;
  509. }
  510. - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  511. progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  512. destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
  513. completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
  514. {
  515. AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
  516. delegate.manager = self;
  517. delegate.completionHandler = completionHandler;
  518. if (destination) {
  519. delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
  520. return destination(location, task.response);
  521. };
  522. }
  523. downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
  524. [self setDelegate:delegate forTask:downloadTask];
  525. delegate.downloadProgressBlock = downloadProgressBlock;
  526. }
  527. - (void)removeDelegateForTask:(NSURLSessionTask *)task {
  528. NSParameterAssert(task);
  529. [self.lock lock];
  530. [self removeNotificationObserverForTask:task];
  531. [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
  532. [self.lock unlock];
  533. }
  534. #pragma mark -
  535. - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
  536. __block NSArray *tasks = nil;
  537. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  538. [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
  539. if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
  540. tasks = dataTasks;
  541. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
  542. tasks = uploadTasks;
  543. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
  544. tasks = downloadTasks;
  545. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
  546. tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
  547. }
  548. dispatch_semaphore_signal(semaphore);
  549. }];
  550. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  551. return tasks;
  552. }
  553. - (NSArray *)tasks {
  554. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  555. }
  556. - (NSArray *)dataTasks {
  557. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  558. }
  559. - (NSArray *)uploadTasks {
  560. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  561. }
  562. - (NSArray *)downloadTasks {
  563. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  564. }
  565. #pragma mark -
  566. - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession {
  567. if (cancelPendingTasks) {
  568. [self.session invalidateAndCancel];
  569. } else {
  570. [self.session finishTasksAndInvalidate];
  571. }
  572. if (resetSession) {
  573. self.session = nil;
  574. }
  575. }
  576. #pragma mark -
  577. - (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
  578. NSParameterAssert(responseSerializer);
  579. _responseSerializer = responseSerializer;
  580. }
  581. #pragma mark -
  582. - (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
  583. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
  584. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
  585. }
  586. - (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
  587. [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
  588. [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
  589. }
  590. #pragma mark -
  591. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
  592. uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  593. downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  594. completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
  595. NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
  596. [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
  597. return dataTask;
  598. }
  599. #pragma mark -
  600. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
  601. fromFile:(NSURL *)fileURL
  602. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  603. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  604. {
  605. NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
  606. if (uploadTask) {
  607. [self addDelegateForUploadTask:uploadTask
  608. progress:uploadProgressBlock
  609. completionHandler:completionHandler];
  610. }
  611. return uploadTask;
  612. }
  613. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
  614. fromData:(NSData *)bodyData
  615. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  616. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  617. {
  618. NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
  619. [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
  620. return uploadTask;
  621. }
  622. - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
  623. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  624. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  625. {
  626. NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
  627. [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
  628. return uploadTask;
  629. }
  630. #pragma mark -
  631. - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
  632. progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  633. destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
  634. completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
  635. {
  636. NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
  637. [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
  638. return downloadTask;
  639. }
  640. - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
  641. progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  642. destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
  643. completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
  644. {
  645. NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
  646. [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
  647. return downloadTask;
  648. }
  649. #pragma mark -
  650. - (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
  651. return [[self delegateForTask:task] uploadProgress];
  652. }
  653. - (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
  654. return [[self delegateForTask:task] downloadProgress];
  655. }
  656. #pragma mark -
  657. - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
  658. self.sessionDidBecomeInvalid = block;
  659. }
  660. - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
  661. self.sessionDidReceiveAuthenticationChallenge = block;
  662. }
  663. #if !TARGET_OS_OSX
  664. - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
  665. self.didFinishEventsForBackgroundURLSession = block;
  666. }
  667. #endif
  668. #pragma mark -
  669. - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
  670. self.taskNeedNewBodyStream = block;
  671. }
  672. - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
  673. self.taskWillPerformHTTPRedirection = block;
  674. }
  675. - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
  676. self.taskDidSendBodyData = block;
  677. }
  678. - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
  679. self.taskDidComplete = block;
  680. }
  681. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  682. - (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
  683. self.taskDidFinishCollectingMetrics = block;
  684. }
  685. #endif
  686. #pragma mark -
  687. - (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
  688. self.dataTaskDidReceiveResponse = block;
  689. }
  690. - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
  691. self.dataTaskDidBecomeDownloadTask = block;
  692. }
  693. - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
  694. self.dataTaskDidReceiveData = block;
  695. }
  696. - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
  697. self.dataTaskWillCacheResponse = block;
  698. }
  699. #pragma mark -
  700. - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
  701. self.downloadTaskDidFinishDownloading = block;
  702. }
  703. - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
  704. self.downloadTaskDidWriteData = block;
  705. }
  706. - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
  707. self.downloadTaskDidResume = block;
  708. }
  709. #pragma mark - NSObject
  710. - (NSString *)description {
  711. return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
  712. }
  713. - (BOOL)respondsToSelector:(SEL)selector {
  714. if (selector == @selector(URLSession:didReceiveChallenge:completionHandler:)) {
  715. return self.sessionDidReceiveAuthenticationChallenge != nil;
  716. } else if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
  717. return self.taskWillPerformHTTPRedirection != nil;
  718. } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
  719. return self.dataTaskDidReceiveResponse != nil;
  720. } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
  721. return self.dataTaskWillCacheResponse != nil;
  722. }
  723. #if !TARGET_OS_OSX
  724. else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
  725. return self.didFinishEventsForBackgroundURLSession != nil;
  726. }
  727. #endif
  728. return [[self class] instancesRespondToSelector:selector];
  729. }
  730. #pragma mark - NSURLSessionDelegate
  731. - (void)URLSession:(NSURLSession *)session
  732. didBecomeInvalidWithError:(NSError *)error
  733. {
  734. if (self.sessionDidBecomeInvalid) {
  735. self.sessionDidBecomeInvalid(session, error);
  736. }
  737. [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
  738. }
  739. - (void)URLSession:(NSURLSession *)session
  740. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  741. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  742. {
  743. NSAssert(self.sessionDidReceiveAuthenticationChallenge != nil, @"`respondsToSelector:` implementation forces `URLSession:didReceiveChallenge:completionHandler:` to be called only if `self.sessionDidReceiveAuthenticationChallenge` is not nil");
  744. NSURLCredential *credential = nil;
  745. NSURLSessionAuthChallengeDisposition disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
  746. if (completionHandler) {
  747. completionHandler(disposition, credential);
  748. }
  749. }
  750. #pragma mark - NSURLSessionTaskDelegate
  751. - (void)URLSession:(NSURLSession *)session
  752. task:(NSURLSessionTask *)task
  753. willPerformHTTPRedirection:(NSHTTPURLResponse *)response
  754. newRequest:(NSURLRequest *)request
  755. completionHandler:(void (^)(NSURLRequest *))completionHandler
  756. {
  757. NSURLRequest *redirectRequest = request;
  758. if (self.taskWillPerformHTTPRedirection) {
  759. redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
  760. }
  761. if (completionHandler) {
  762. completionHandler(redirectRequest);
  763. }
  764. }
  765. - (void)URLSession:(NSURLSession *)session
  766. task:(NSURLSessionTask *)task
  767. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  768. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  769. {
  770. BOOL evaluateServerTrust = NO;
  771. NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  772. NSURLCredential *credential = nil;
  773. if (self.authenticationChallengeHandler) {
  774. id result = self.authenticationChallengeHandler(session, task, challenge, completionHandler);
  775. if (result == nil) {
  776. return;
  777. } else if ([result isKindOfClass:NSError.class]) {
  778. objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN);
  779. disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
  780. } else if ([result isKindOfClass:NSURLCredential.class]) {
  781. credential = result;
  782. disposition = NSURLSessionAuthChallengeUseCredential;
  783. } else if ([result isKindOfClass:NSNumber.class]) {
  784. disposition = [result integerValue];
  785. NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @"");
  786. evaluateServerTrust = disposition == NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
  787. } else {
  788. @throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil];
  789. }
  790. } else {
  791. evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
  792. }
  793. if (evaluateServerTrust) {
  794. if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
  795. disposition = NSURLSessionAuthChallengeUseCredential;
  796. credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  797. } else {
  798. objc_setAssociatedObject(task, AuthenticationChallengeErrorKey,
  799. [self serverTrustErrorForServerTrust:challenge.protectionSpace.serverTrust url:task.currentRequest.URL],
  800. OBJC_ASSOCIATION_RETAIN);
  801. disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
  802. }
  803. }
  804. if (completionHandler) {
  805. completionHandler(disposition, credential);
  806. }
  807. }
  808. - (nonnull NSError *)serverTrustErrorForServerTrust:(nullable SecTrustRef)serverTrust url:(nullable NSURL *)url
  809. {
  810. NSBundle *CFNetworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.CFNetwork"];
  811. NSString *defaultValue = @"The certificate for this server is invalid. You might be connecting to a server that is pretending to be “%@” which could put your confidential information at risk.";
  812. NSString *descriptionFormat = NSLocalizedStringWithDefaultValue(@"Err-1202.w", nil, CFNetworkBundle, defaultValue, @"") ?: defaultValue;
  813. NSString *localizedDescription = [descriptionFormat componentsSeparatedByString:@"%@"].count <= 2 ? [NSString localizedStringWithFormat:descriptionFormat, url.host] : descriptionFormat;
  814. NSMutableDictionary *userInfo = [@{
  815. NSLocalizedDescriptionKey: localizedDescription
  816. } mutableCopy];
  817. if (serverTrust) {
  818. userInfo[NSURLErrorFailingURLPeerTrustErrorKey] = (__bridge id)serverTrust;
  819. }
  820. if (url) {
  821. userInfo[NSURLErrorFailingURLErrorKey] = url;
  822. if (url.absoluteString) {
  823. userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString;
  824. }
  825. }
  826. return [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorServerCertificateUntrusted userInfo:userInfo];
  827. }
  828. - (void)URLSession:(NSURLSession *)session
  829. task:(NSURLSessionTask *)task
  830. needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
  831. {
  832. NSInputStream *inputStream = nil;
  833. if (self.taskNeedNewBodyStream) {
  834. inputStream = self.taskNeedNewBodyStream(session, task);
  835. } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
  836. inputStream = [task.originalRequest.HTTPBodyStream copy];
  837. }
  838. if (completionHandler) {
  839. completionHandler(inputStream);
  840. }
  841. }
  842. - (void)URLSession:(NSURLSession *)session
  843. task:(NSURLSessionTask *)task
  844. didSendBodyData:(int64_t)bytesSent
  845. totalBytesSent:(int64_t)totalBytesSent
  846. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
  847. {
  848. int64_t totalUnitCount = totalBytesExpectedToSend;
  849. if (totalUnitCount == NSURLSessionTransferSizeUnknown) {
  850. NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
  851. if (contentLength) {
  852. totalUnitCount = (int64_t) [contentLength longLongValue];
  853. }
  854. }
  855. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  856. if (delegate) {
  857. [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
  858. }
  859. if (self.taskDidSendBodyData) {
  860. self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
  861. }
  862. }
  863. - (void)URLSession:(NSURLSession *)session
  864. task:(NSURLSessionTask *)task
  865. didCompleteWithError:(NSError *)error
  866. {
  867. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  868. // delegate may be nil when completing a task in the background
  869. if (delegate) {
  870. [delegate URLSession:session task:task didCompleteWithError:error];
  871. [self removeDelegateForTask:task];
  872. }
  873. if (self.taskDidComplete) {
  874. self.taskDidComplete(session, task, error);
  875. }
  876. }
  877. #if AF_CAN_INCLUDE_SESSION_TASK_METRICS
  878. - (void)URLSession:(NSURLSession *)session
  879. task:(NSURLSessionTask *)task
  880. didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10))
  881. {
  882. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  883. // Metrics may fire after URLSession:task:didCompleteWithError: is called, delegate may be nil
  884. if (delegate) {
  885. [delegate URLSession:session task:task didFinishCollectingMetrics:metrics];
  886. }
  887. if (self.taskDidFinishCollectingMetrics) {
  888. self.taskDidFinishCollectingMetrics(session, task, metrics);
  889. }
  890. }
  891. #endif
  892. #pragma mark - NSURLSessionDataDelegate
  893. - (void)URLSession:(NSURLSession *)session
  894. dataTask:(NSURLSessionDataTask *)dataTask
  895. didReceiveResponse:(NSURLResponse *)response
  896. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
  897. {
  898. NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
  899. if (self.dataTaskDidReceiveResponse) {
  900. disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
  901. }
  902. if (completionHandler) {
  903. completionHandler(disposition);
  904. }
  905. }
  906. - (void)URLSession:(NSURLSession *)session
  907. dataTask:(NSURLSessionDataTask *)dataTask
  908. didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  909. {
  910. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
  911. if (delegate) {
  912. [self removeDelegateForTask:dataTask];
  913. [self setDelegate:delegate forTask:downloadTask];
  914. }
  915. if (self.dataTaskDidBecomeDownloadTask) {
  916. self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
  917. }
  918. }
  919. - (void)URLSession:(NSURLSession *)session
  920. dataTask:(NSURLSessionDataTask *)dataTask
  921. didReceiveData:(NSData *)data
  922. {
  923. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
  924. [delegate URLSession:session dataTask:dataTask didReceiveData:data];
  925. if (self.dataTaskDidReceiveData) {
  926. self.dataTaskDidReceiveData(session, dataTask, data);
  927. }
  928. }
  929. - (void)URLSession:(NSURLSession *)session
  930. dataTask:(NSURLSessionDataTask *)dataTask
  931. willCacheResponse:(NSCachedURLResponse *)proposedResponse
  932. completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
  933. {
  934. NSCachedURLResponse *cachedResponse = proposedResponse;
  935. if (self.dataTaskWillCacheResponse) {
  936. cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
  937. }
  938. if (completionHandler) {
  939. completionHandler(cachedResponse);
  940. }
  941. }
  942. #if !TARGET_OS_OSX
  943. - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
  944. if (self.didFinishEventsForBackgroundURLSession) {
  945. dispatch_async(dispatch_get_main_queue(), ^{
  946. self.didFinishEventsForBackgroundURLSession(session);
  947. });
  948. }
  949. }
  950. #endif
  951. #pragma mark - NSURLSessionDownloadDelegate
  952. - (void)URLSession:(NSURLSession *)session
  953. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  954. didFinishDownloadingToURL:(NSURL *)location
  955. {
  956. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  957. if (self.downloadTaskDidFinishDownloading) {
  958. NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
  959. if (fileURL) {
  960. delegate.downloadFileURL = fileURL;
  961. NSError *error = nil;
  962. if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
  963. [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
  964. } else {
  965. [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
  966. }
  967. return;
  968. }
  969. }
  970. if (delegate) {
  971. [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
  972. }
  973. }
  974. - (void)URLSession:(NSURLSession *)session
  975. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  976. didWriteData:(int64_t)bytesWritten
  977. totalBytesWritten:(int64_t)totalBytesWritten
  978. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
  979. {
  980. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  981. if (delegate) {
  982. [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  983. }
  984. if (self.downloadTaskDidWriteData) {
  985. self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
  986. }
  987. }
  988. - (void)URLSession:(NSURLSession *)session
  989. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  990. didResumeAtOffset:(int64_t)fileOffset
  991. expectedTotalBytes:(int64_t)expectedTotalBytes
  992. {
  993. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  994. if (delegate) {
  995. [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
  996. }
  997. if (self.downloadTaskDidResume) {
  998. self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
  999. }
  1000. }
  1001. #pragma mark - NSSecureCoding
  1002. + (BOOL)supportsSecureCoding {
  1003. return YES;
  1004. }
  1005. - (instancetype)initWithCoder:(NSCoder *)decoder {
  1006. NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
  1007. self = [self initWithSessionConfiguration:configuration];
  1008. if (!self) {
  1009. return nil;
  1010. }
  1011. return self;
  1012. }
  1013. - (void)encodeWithCoder:(NSCoder *)coder {
  1014. [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
  1015. }
  1016. #pragma mark - NSCopying
  1017. - (instancetype)copyWithZone:(NSZone *)zone {
  1018. return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
  1019. }
  1020. @end