2
0

WFAFURLSessionManager.m 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  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 "WFAFURLSessionManager.h"
  22. #import <objc/runtime.h>
  23. #ifndef NSFoundationVersionNumber_iOS_8_0
  24. #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
  25. #else
  26. #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
  27. #endif
  28. static dispatch_queue_t url_session_manager_creation_queue() {
  29. static dispatch_queue_t af_url_session_manager_creation_queue;
  30. static dispatch_once_t onceToken;
  31. dispatch_once(&onceToken, ^{
  32. af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
  33. });
  34. return af_url_session_manager_creation_queue;
  35. }
  36. static void url_session_manager_create_task_safely(dispatch_block_t block) {
  37. if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
  38. // Fix of bug
  39. // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
  40. // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
  41. dispatch_sync(url_session_manager_creation_queue(), block);
  42. } else {
  43. block();
  44. }
  45. }
  46. static dispatch_queue_t url_session_manager_processing_queue() {
  47. static dispatch_queue_t af_url_session_manager_processing_queue;
  48. static dispatch_once_t onceToken;
  49. dispatch_once(&onceToken, ^{
  50. af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
  51. });
  52. return af_url_session_manager_processing_queue;
  53. }
  54. static dispatch_group_t url_session_manager_completion_group() {
  55. static dispatch_group_t af_url_session_manager_completion_group;
  56. static dispatch_once_t onceToken;
  57. dispatch_once(&onceToken, ^{
  58. af_url_session_manager_completion_group = dispatch_group_create();
  59. });
  60. return af_url_session_manager_completion_group;
  61. }
  62. NSString * const WFAFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
  63. NSString * const WFAFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
  64. NSString * const WFAFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
  65. NSString * const WFAFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
  66. NSString * const WFAFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
  67. NSString * const WFAFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
  68. NSString * const WFAFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
  69. NSString * const WFAFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
  70. NSString * const WFAFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
  71. NSString * const WFAFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
  72. static NSString * const WFAFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
  73. static NSUInteger const WFAFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
  74. typedef void (^WFAFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
  75. typedef NSURLSessionAuthChallengeDisposition (^WFAFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
  76. typedef NSURLRequest * (^WFAFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
  77. typedef NSURLSessionAuthChallengeDisposition (^WFAFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
  78. typedef void (^WFAFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
  79. typedef NSInputStream * (^WFAFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
  80. typedef void (^WFAFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
  81. typedef void (^WFAFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
  82. typedef NSURLSessionResponseDisposition (^WFAFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
  83. typedef void (^WFAFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
  84. typedef void (^WFAFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
  85. typedef NSCachedURLResponse * (^WFAFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);
  86. typedef NSURL * (^WFAFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
  87. typedef void (^WFAFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
  88. typedef void (^WFAFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
  89. typedef void (^WFAFURLSessionTaskProgressBlock)(NSProgress *);
  90. typedef void (^WFAFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
  91. #pragma mark -
  92. @interface WFAFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
  93. - (instancetype)initWithTask:(NSURLSessionTask *)task;
  94. @property (nonatomic, weak) WFAFURLSessionManager *manager;
  95. @property (nonatomic, strong) NSMutableData *mutableData;
  96. @property (nonatomic, strong) NSProgress *uploadProgress;
  97. @property (nonatomic, strong) NSProgress *downloadProgress;
  98. @property (nonatomic, copy) NSURL *downloadFileURL;
  99. @property (nonatomic, copy) WFAFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
  100. @property (nonatomic, copy) WFAFURLSessionTaskProgressBlock uploadProgressBlock;
  101. @property (nonatomic, copy) WFAFURLSessionTaskProgressBlock downloadProgressBlock;
  102. @property (nonatomic, copy) WFAFURLSessionTaskCompletionHandler completionHandler;
  103. @end
  104. @implementation WFAFURLSessionManagerTaskDelegate
  105. - (instancetype)initWithTask:(NSURLSessionTask *)task {
  106. self = [super init];
  107. if (!self) {
  108. return nil;
  109. }
  110. _mutableData = [NSMutableData data];
  111. _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
  112. _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
  113. __weak __typeof__(task) weakTask = task;
  114. for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
  115. {
  116. progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
  117. progress.cancellable = YES;
  118. progress.cancellationHandler = ^{
  119. [weakTask cancel];
  120. };
  121. progress.pausable = YES;
  122. progress.pausingHandler = ^{
  123. [weakTask suspend];
  124. };
  125. if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
  126. progress.resumingHandler = ^{
  127. [weakTask resume];
  128. };
  129. }
  130. [progress addObserver:self
  131. forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
  132. options:NSKeyValueObservingOptionNew
  133. context:NULL];
  134. }
  135. return self;
  136. }
  137. - (void)dealloc {
  138. [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
  139. [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
  140. }
  141. #pragma mark - NSProgress Tracking
  142. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  143. if ([object isEqual:self.downloadProgress]) {
  144. if (self.downloadProgressBlock) {
  145. self.downloadProgressBlock(object);
  146. }
  147. }
  148. else if ([object isEqual:self.uploadProgress]) {
  149. if (self.uploadProgressBlock) {
  150. self.uploadProgressBlock(object);
  151. }
  152. }
  153. }
  154. #pragma mark - NSURLSessionTaskDelegate
  155. - (void)URLSession:(__unused NSURLSession *)session
  156. task:(NSURLSessionTask *)task
  157. didCompleteWithError:(NSError *)error
  158. {
  159. __strong WFAFURLSessionManager *manager = self.manager;
  160. __block id responseObject = nil;
  161. __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
  162. userInfo[WFAFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
  163. //Performance Improvement from #2672
  164. NSData *data = nil;
  165. if (self.mutableData) {
  166. data = [self.mutableData copy];
  167. //We no longer need the reference, so nil it out to gain back some memory.
  168. self.mutableData = nil;
  169. }
  170. if (self.downloadFileURL) {
  171. userInfo[WFAFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
  172. } else if (data) {
  173. userInfo[WFAFNetworkingTaskDidCompleteResponseDataKey] = data;
  174. }
  175. if (error) {
  176. userInfo[WFAFNetworkingTaskDidCompleteErrorKey] = error;
  177. dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
  178. if (self.completionHandler) {
  179. self.completionHandler(task.response, responseObject, error);
  180. }
  181. dispatch_async(dispatch_get_main_queue(), ^{
  182. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
  183. });
  184. });
  185. } else {
  186. dispatch_async(url_session_manager_processing_queue(), ^{
  187. NSError *serializationError = nil;
  188. responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
  189. if (self.downloadFileURL) {
  190. responseObject = self.downloadFileURL;
  191. }
  192. if (responseObject) {
  193. userInfo[WFAFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
  194. }
  195. if (serializationError) {
  196. userInfo[WFAFNetworkingTaskDidCompleteErrorKey] = serializationError;
  197. }
  198. dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
  199. if (self.completionHandler) {
  200. self.completionHandler(task.response, responseObject, serializationError);
  201. }
  202. dispatch_async(dispatch_get_main_queue(), ^{
  203. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
  204. });
  205. });
  206. });
  207. }
  208. }
  209. #pragma mark - NSURLSessionDataDelegate
  210. - (void)URLSession:(__unused NSURLSession *)session
  211. dataTask:(__unused NSURLSessionDataTask *)dataTask
  212. didReceiveData:(NSData *)data
  213. {
  214. self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
  215. self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
  216. [self.mutableData appendData:data];
  217. }
  218. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
  219. didSendBodyData:(int64_t)bytesSent
  220. totalBytesSent:(int64_t)totalBytesSent
  221. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  222. self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
  223. self.uploadProgress.completedUnitCount = task.countOfBytesSent;
  224. }
  225. #pragma mark - NSURLSessionDownloadDelegate
  226. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
  227. didWriteData:(int64_t)bytesWritten
  228. totalBytesWritten:(int64_t)totalBytesWritten
  229. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
  230. self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
  231. self.downloadProgress.completedUnitCount = totalBytesWritten;
  232. }
  233. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
  234. didResumeAtOffset:(int64_t)fileOffset
  235. expectedTotalBytes:(int64_t)expectedTotalBytes{
  236. self.downloadProgress.totalUnitCount = expectedTotalBytes;
  237. self.downloadProgress.completedUnitCount = fileOffset;
  238. }
  239. - (void)URLSession:(NSURLSession *)session
  240. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  241. didFinishDownloadingToURL:(NSURL *)location
  242. {
  243. self.downloadFileURL = nil;
  244. if (self.downloadTaskDidFinishDownloading) {
  245. self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
  246. if (self.downloadFileURL) {
  247. NSError *fileManagerError = nil;
  248. if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
  249. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
  250. }
  251. }
  252. }
  253. }
  254. @end
  255. #pragma mark -
  256. /**
  257. * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`.
  258. *
  259. * See:
  260. * - https://github.com/AFNetworking/AFNetworking/issues/1477
  261. * - https://github.com/AFNetworking/AFNetworking/issues/2638
  262. * - https://github.com/AFNetworking/AFNetworking/pull/2702
  263. */
  264. static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
  265. Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
  266. Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
  267. method_exchangeImplementations(originalMethod, swizzledMethod);
  268. }
  269. static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
  270. return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
  271. }
  272. static NSString * const WFAFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume";
  273. static NSString * const WFAFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
  274. @interface _WFAFURLSessionTaskSwizzling : NSObject
  275. @end
  276. @implementation _WFAFURLSessionTaskSwizzling
  277. + (void)load {
  278. /**
  279. WARNING: Trouble Ahead
  280. https://github.com/AFNetworking/AFNetworking/pull/2702
  281. */
  282. if (NSClassFromString(@"NSURLSessionTask")) {
  283. /**
  284. iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky.
  285. Many Unit Tests have been built to validate as much of this behavior has possible.
  286. Here is what we know:
  287. - 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.
  288. - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there.
  289. - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`.
  290. - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`.
  291. - 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.
  292. - 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.
  293. - 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.
  294. Some Assumptions:
  295. - 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.
  296. - No background task classes override `resume` or `suspend`
  297. The current solution:
  298. 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
  299. 2) Grab a pointer to the original implementation of `af_resume`
  300. 3) Check to see if the current class has an implementation of resume. If so, continue to step 4.
  301. 4) Grab the super class of the current class.
  302. 5) Grab a pointer for the current class to the current implementation of `resume`.
  303. 6) Grab a pointer for the super class to the current implementation of `resume`.
  304. 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
  305. 8) Set the current class to the super class, and repeat steps 3-8
  306. */
  307. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
  308. NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
  309. #pragma GCC diagnostic push
  310. #pragma GCC diagnostic ignored "-Wnonnull"
  311. NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
  312. #pragma clang diagnostic pop
  313. IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
  314. Class currentClass = [localDataTask class];
  315. while (class_getInstanceMethod(currentClass, @selector(resume))) {
  316. Class superClass = [currentClass superclass];
  317. IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
  318. IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
  319. if (classResumeIMP != superclassResumeIMP &&
  320. originalAFResumeIMP != classResumeIMP) {
  321. [self swizzleResumeAndSuspendMethodForClass:currentClass];
  322. }
  323. currentClass = [currentClass superclass];
  324. }
  325. [localDataTask cancel];
  326. [session finishTasksAndInvalidate];
  327. }
  328. }
  329. + (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
  330. Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
  331. Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
  332. if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
  333. af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
  334. }
  335. if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
  336. af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
  337. }
  338. }
  339. - (NSURLSessionTaskState)state {
  340. NSAssert(NO, @"State method should never be called in the actual dummy class");
  341. return NSURLSessionTaskStateCanceling;
  342. }
  343. - (void)af_resume {
  344. NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
  345. NSURLSessionTaskState state = [self state];
  346. [self af_resume];
  347. if (state != NSURLSessionTaskStateRunning) {
  348. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNSURLSessionTaskDidResumeNotification object:self];
  349. }
  350. }
  351. - (void)af_suspend {
  352. NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
  353. NSURLSessionTaskState state = [self state];
  354. [self af_suspend];
  355. if (state != NSURLSessionTaskStateSuspended) {
  356. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNSURLSessionTaskDidSuspendNotification object:self];
  357. }
  358. }
  359. @end
  360. #pragma mark -
  361. @interface WFAFURLSessionManager ()
  362. @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
  363. @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
  364. @property (readwrite, nonatomic, strong) NSURLSession *session;
  365. @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
  366. @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
  367. @property (readwrite, nonatomic, strong) NSLock *lock;
  368. @property (readwrite, nonatomic, copy) WFAFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
  369. @property (readwrite, nonatomic, copy) WFAFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
  370. @property (readwrite, nonatomic, copy) WFAFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
  371. @property (readwrite, nonatomic, copy) WFAFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
  372. @property (readwrite, nonatomic, copy) WFAFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
  373. @property (readwrite, nonatomic, copy) WFAFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
  374. @property (readwrite, nonatomic, copy) WFAFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
  375. @property (readwrite, nonatomic, copy) WFAFURLSessionTaskDidCompleteBlock taskDidComplete;
  376. @property (readwrite, nonatomic, copy) WFAFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
  377. @property (readwrite, nonatomic, copy) WFAFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
  378. @property (readwrite, nonatomic, copy) WFAFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
  379. @property (readwrite, nonatomic, copy) WFAFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
  380. @property (readwrite, nonatomic, copy) WFAFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
  381. @property (readwrite, nonatomic, copy) WFAFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
  382. @property (readwrite, nonatomic, copy) WFAFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
  383. @end
  384. @implementation WFAFURLSessionManager
  385. - (instancetype)init {
  386. return [self initWithSessionConfiguration:nil];
  387. }
  388. - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
  389. self = [super init];
  390. if (!self) {
  391. return nil;
  392. }
  393. if (!configuration) {
  394. configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  395. }
  396. self.sessionConfiguration = configuration;
  397. self.operationQueue = [[NSOperationQueue alloc] init];
  398. self.operationQueue.maxConcurrentOperationCount = 1;
  399. self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
  400. self.responseSerializer = [WFAFJSONResponseSerializer serializer];
  401. self.securityPolicy = [WFAFSecurityPolicy defaultPolicy];
  402. #if !TARGET_OS_WATCH
  403. self.reachabilityManager = [WFAFNetworkReachabilityManager sharedManager];
  404. #endif
  405. self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
  406. self.lock = [[NSLock alloc] init];
  407. self.lock.name = WFAFURLSessionManagerLockName;
  408. [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
  409. for (NSURLSessionDataTask *task in dataTasks) {
  410. [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
  411. }
  412. for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
  413. [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
  414. }
  415. for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
  416. [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
  417. }
  418. }];
  419. return self;
  420. }
  421. - (void)dealloc {
  422. [[NSNotificationCenter defaultCenter] removeObserver:self];
  423. }
  424. #pragma mark -
  425. - (NSString *)taskDescriptionForSessionTasks {
  426. return [NSString stringWithFormat:@"%p", self];
  427. }
  428. - (void)taskDidResume:(NSNotification *)notification {
  429. NSURLSessionTask *task = notification.object;
  430. if ([task respondsToSelector:@selector(taskDescription)]) {
  431. if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
  432. dispatch_async(dispatch_get_main_queue(), ^{
  433. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNetworkingTaskDidResumeNotification object:task];
  434. });
  435. }
  436. }
  437. }
  438. - (void)taskDidSuspend:(NSNotification *)notification {
  439. NSURLSessionTask *task = notification.object;
  440. if ([task respondsToSelector:@selector(taskDescription)]) {
  441. if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
  442. dispatch_async(dispatch_get_main_queue(), ^{
  443. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFNetworkingTaskDidSuspendNotification object:task];
  444. });
  445. }
  446. }
  447. }
  448. #pragma mark -
  449. - (WFAFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
  450. NSParameterAssert(task);
  451. WFAFURLSessionManagerTaskDelegate *delegate = nil;
  452. [self.lock lock];
  453. delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
  454. [self.lock unlock];
  455. return delegate;
  456. }
  457. - (void)setDelegate:(WFAFURLSessionManagerTaskDelegate *)delegate
  458. forTask:(NSURLSessionTask *)task
  459. {
  460. NSParameterAssert(task);
  461. NSParameterAssert(delegate);
  462. [self.lock lock];
  463. self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
  464. [self addNotificationObserverForTask:task];
  465. [self.lock unlock];
  466. }
  467. - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
  468. uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  469. downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  470. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  471. {
  472. WFAFURLSessionManagerTaskDelegate *delegate = [[WFAFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
  473. delegate.manager = self;
  474. delegate.completionHandler = completionHandler;
  475. dataTask.taskDescription = self.taskDescriptionForSessionTasks;
  476. [self setDelegate:delegate forTask:dataTask];
  477. delegate.uploadProgressBlock = uploadProgressBlock;
  478. delegate.downloadProgressBlock = downloadProgressBlock;
  479. }
  480. - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
  481. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  482. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  483. {
  484. WFAFURLSessionManagerTaskDelegate *delegate = [[WFAFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
  485. delegate.manager = self;
  486. delegate.completionHandler = completionHandler;
  487. uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
  488. [self setDelegate:delegate forTask:uploadTask];
  489. delegate.uploadProgressBlock = uploadProgressBlock;
  490. }
  491. - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  492. progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  493. destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
  494. completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
  495. {
  496. WFAFURLSessionManagerTaskDelegate *delegate = [[WFAFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
  497. delegate.manager = self;
  498. delegate.completionHandler = completionHandler;
  499. if (destination) {
  500. delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
  501. return destination(location, task.response);
  502. };
  503. }
  504. downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
  505. [self setDelegate:delegate forTask:downloadTask];
  506. delegate.downloadProgressBlock = downloadProgressBlock;
  507. }
  508. - (void)removeDelegateForTask:(NSURLSessionTask *)task {
  509. NSParameterAssert(task);
  510. [self.lock lock];
  511. [self removeNotificationObserverForTask:task];
  512. [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
  513. [self.lock unlock];
  514. }
  515. #pragma mark -
  516. - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
  517. __block NSArray *tasks = nil;
  518. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  519. [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
  520. if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
  521. tasks = dataTasks;
  522. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
  523. tasks = uploadTasks;
  524. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
  525. tasks = downloadTasks;
  526. } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
  527. tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
  528. }
  529. dispatch_semaphore_signal(semaphore);
  530. }];
  531. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  532. return tasks;
  533. }
  534. - (NSArray *)tasks {
  535. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  536. }
  537. - (NSArray *)dataTasks {
  538. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  539. }
  540. - (NSArray *)uploadTasks {
  541. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  542. }
  543. - (NSArray *)downloadTasks {
  544. return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
  545. }
  546. #pragma mark -
  547. - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
  548. if (cancelPendingTasks) {
  549. [self.session invalidateAndCancel];
  550. } else {
  551. [self.session finishTasksAndInvalidate];
  552. }
  553. }
  554. #pragma mark -
  555. - (void)setResponseSerializer:(id <WFAFURLResponseSerialization>)responseSerializer {
  556. NSParameterAssert(responseSerializer);
  557. _responseSerializer = responseSerializer;
  558. }
  559. #pragma mark -
  560. - (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
  561. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:WFAFNSURLSessionTaskDidResumeNotification object:task];
  562. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:WFAFNSURLSessionTaskDidSuspendNotification object:task];
  563. }
  564. - (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
  565. [[NSNotificationCenter defaultCenter] removeObserver:self name:WFAFNSURLSessionTaskDidSuspendNotification object:task];
  566. [[NSNotificationCenter defaultCenter] removeObserver:self name:WFAFNSURLSessionTaskDidResumeNotification object:task];
  567. }
  568. #pragma mark -
  569. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
  570. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  571. {
  572. return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
  573. }
  574. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
  575. uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  576. downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  577. completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
  578. __block NSURLSessionDataTask *dataTask = nil;
  579. url_session_manager_create_task_safely(^{
  580. dataTask = [self.session dataTaskWithRequest:request];
  581. });
  582. [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
  583. return dataTask;
  584. }
  585. #pragma mark -
  586. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
  587. fromFile:(NSURL *)fileURL
  588. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  589. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  590. {
  591. __block NSURLSessionUploadTask *uploadTask = nil;
  592. url_session_manager_create_task_safely(^{
  593. uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
  594. });
  595. // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
  596. if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
  597. for (NSUInteger attempts = 0; !uploadTask && attempts < WFAFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
  598. uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
  599. }
  600. }
  601. [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
  602. return uploadTask;
  603. }
  604. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
  605. fromData:(NSData *)bodyData
  606. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  607. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  608. {
  609. __block NSURLSessionUploadTask *uploadTask = nil;
  610. url_session_manager_create_task_safely(^{
  611. uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
  612. });
  613. [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
  614. return uploadTask;
  615. }
  616. - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
  617. progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  618. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  619. {
  620. __block NSURLSessionUploadTask *uploadTask = nil;
  621. url_session_manager_create_task_safely(^{
  622. uploadTask = [self.session uploadTaskWithStreamedRequest:request];
  623. });
  624. [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
  625. return uploadTask;
  626. }
  627. #pragma mark -
  628. - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
  629. progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  630. destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
  631. completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
  632. {
  633. __block NSURLSessionDownloadTask *downloadTask = nil;
  634. url_session_manager_create_task_safely(^{
  635. downloadTask = [self.session downloadTaskWithRequest:request];
  636. });
  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. __block NSURLSessionDownloadTask *downloadTask = nil;
  646. url_session_manager_create_task_safely(^{
  647. downloadTask = [self.session downloadTaskWithResumeData:resumeData];
  648. });
  649. [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
  650. return downloadTask;
  651. }
  652. #pragma mark -
  653. - (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
  654. return [[self delegateForTask:task] uploadProgress];
  655. }
  656. - (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
  657. return [[self delegateForTask:task] downloadProgress];
  658. }
  659. #pragma mark -
  660. - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
  661. self.sessionDidBecomeInvalid = block;
  662. }
  663. - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
  664. self.sessionDidReceiveAuthenticationChallenge = block;
  665. }
  666. - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
  667. self.didFinishEventsForBackgroundURLSession = block;
  668. }
  669. #pragma mark -
  670. - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
  671. self.taskNeedNewBodyStream = block;
  672. }
  673. - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
  674. self.taskWillPerformHTTPRedirection = block;
  675. }
  676. - (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
  677. self.taskDidReceiveAuthenticationChallenge = block;
  678. }
  679. - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
  680. self.taskDidSendBodyData = block;
  681. }
  682. - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
  683. self.taskDidComplete = block;
  684. }
  685. #pragma mark -
  686. - (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
  687. self.dataTaskDidReceiveResponse = block;
  688. }
  689. - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
  690. self.dataTaskDidBecomeDownloadTask = block;
  691. }
  692. - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
  693. self.dataTaskDidReceiveData = block;
  694. }
  695. - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
  696. self.dataTaskWillCacheResponse = block;
  697. }
  698. #pragma mark -
  699. - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
  700. self.downloadTaskDidFinishDownloading = block;
  701. }
  702. - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
  703. self.downloadTaskDidWriteData = block;
  704. }
  705. - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
  706. self.downloadTaskDidResume = block;
  707. }
  708. #pragma mark - NSObject
  709. - (NSString *)description {
  710. return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
  711. }
  712. - (BOOL)respondsToSelector:(SEL)selector {
  713. if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
  714. return self.taskWillPerformHTTPRedirection != nil;
  715. } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
  716. return self.dataTaskDidReceiveResponse != nil;
  717. } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
  718. return self.dataTaskWillCacheResponse != nil;
  719. } else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
  720. return self.didFinishEventsForBackgroundURLSession != nil;
  721. }
  722. return [[self class] instancesRespondToSelector:selector];
  723. }
  724. #pragma mark - NSURLSessionDelegate
  725. - (void)URLSession:(NSURLSession *)session
  726. didBecomeInvalidWithError:(NSError *)error
  727. {
  728. if (self.sessionDidBecomeInvalid) {
  729. self.sessionDidBecomeInvalid(session, error);
  730. }
  731. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFURLSessionDidInvalidateNotification object:session];
  732. }
  733. - (void)URLSession:(NSURLSession *)session
  734. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  735. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  736. {
  737. NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  738. __block NSURLCredential *credential = nil;
  739. if (self.sessionDidReceiveAuthenticationChallenge) {
  740. disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
  741. } else {
  742. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  743. if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
  744. credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  745. if (credential) {
  746. disposition = NSURLSessionAuthChallengeUseCredential;
  747. } else {
  748. disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  749. }
  750. } else {
  751. disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
  752. }
  753. } else {
  754. disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  755. }
  756. }
  757. if (completionHandler) {
  758. completionHandler(disposition, credential);
  759. }
  760. }
  761. #pragma mark - NSURLSessionTaskDelegate
  762. - (void)URLSession:(NSURLSession *)session
  763. task:(NSURLSessionTask *)task
  764. willPerformHTTPRedirection:(NSHTTPURLResponse *)response
  765. newRequest:(NSURLRequest *)request
  766. completionHandler:(void (^)(NSURLRequest *))completionHandler
  767. {
  768. NSURLRequest *redirectRequest = request;
  769. if (self.taskWillPerformHTTPRedirection) {
  770. redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
  771. }
  772. if (completionHandler) {
  773. completionHandler(redirectRequest);
  774. }
  775. }
  776. - (void)URLSession:(NSURLSession *)session
  777. task:(NSURLSessionTask *)task
  778. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  779. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  780. {
  781. NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  782. __block NSURLCredential *credential = nil;
  783. if (self.taskDidReceiveAuthenticationChallenge) {
  784. disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
  785. } else {
  786. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  787. if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
  788. disposition = NSURLSessionAuthChallengeUseCredential;
  789. credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  790. } else {
  791. disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
  792. }
  793. } else {
  794. disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  795. }
  796. }
  797. if (completionHandler) {
  798. completionHandler(disposition, credential);
  799. }
  800. }
  801. - (void)URLSession:(NSURLSession *)session
  802. task:(NSURLSessionTask *)task
  803. needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
  804. {
  805. NSInputStream *inputStream = nil;
  806. if (self.taskNeedNewBodyStream) {
  807. inputStream = self.taskNeedNewBodyStream(session, task);
  808. } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
  809. inputStream = [task.originalRequest.HTTPBodyStream copy];
  810. }
  811. if (completionHandler) {
  812. completionHandler(inputStream);
  813. }
  814. }
  815. - (void)URLSession:(NSURLSession *)session
  816. task:(NSURLSessionTask *)task
  817. didSendBodyData:(int64_t)bytesSent
  818. totalBytesSent:(int64_t)totalBytesSent
  819. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
  820. {
  821. int64_t totalUnitCount = totalBytesExpectedToSend;
  822. if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
  823. NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
  824. if(contentLength) {
  825. totalUnitCount = (int64_t) [contentLength longLongValue];
  826. }
  827. }
  828. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  829. if (delegate) {
  830. [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
  831. }
  832. if (self.taskDidSendBodyData) {
  833. self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
  834. }
  835. }
  836. - (void)URLSession:(NSURLSession *)session
  837. task:(NSURLSessionTask *)task
  838. didCompleteWithError:(NSError *)error
  839. {
  840. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  841. // delegate may be nil when completing a task in the background
  842. if (delegate) {
  843. [delegate URLSession:session task:task didCompleteWithError:error];
  844. [self removeDelegateForTask:task];
  845. }
  846. if (self.taskDidComplete) {
  847. self.taskDidComplete(session, task, error);
  848. }
  849. }
  850. #pragma mark - NSURLSessionDataDelegate
  851. - (void)URLSession:(NSURLSession *)session
  852. dataTask:(NSURLSessionDataTask *)dataTask
  853. didReceiveResponse:(NSURLResponse *)response
  854. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
  855. {
  856. NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
  857. if (self.dataTaskDidReceiveResponse) {
  858. disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
  859. }
  860. if (completionHandler) {
  861. completionHandler(disposition);
  862. }
  863. }
  864. - (void)URLSession:(NSURLSession *)session
  865. dataTask:(NSURLSessionDataTask *)dataTask
  866. didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  867. {
  868. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
  869. if (delegate) {
  870. [self removeDelegateForTask:dataTask];
  871. [self setDelegate:delegate forTask:downloadTask];
  872. }
  873. if (self.dataTaskDidBecomeDownloadTask) {
  874. self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
  875. }
  876. }
  877. - (void)URLSession:(NSURLSession *)session
  878. dataTask:(NSURLSessionDataTask *)dataTask
  879. didReceiveData:(NSData *)data
  880. {
  881. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
  882. [delegate URLSession:session dataTask:dataTask didReceiveData:data];
  883. if (self.dataTaskDidReceiveData) {
  884. self.dataTaskDidReceiveData(session, dataTask, data);
  885. }
  886. }
  887. - (void)URLSession:(NSURLSession *)session
  888. dataTask:(NSURLSessionDataTask *)dataTask
  889. willCacheResponse:(NSCachedURLResponse *)proposedResponse
  890. completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
  891. {
  892. NSCachedURLResponse *cachedResponse = proposedResponse;
  893. if (self.dataTaskWillCacheResponse) {
  894. cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
  895. }
  896. if (completionHandler) {
  897. completionHandler(cachedResponse);
  898. }
  899. }
  900. - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
  901. if (self.didFinishEventsForBackgroundURLSession) {
  902. dispatch_async(dispatch_get_main_queue(), ^{
  903. self.didFinishEventsForBackgroundURLSession(session);
  904. });
  905. }
  906. }
  907. #pragma mark - NSURLSessionDownloadDelegate
  908. - (void)URLSession:(NSURLSession *)session
  909. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  910. didFinishDownloadingToURL:(NSURL *)location
  911. {
  912. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  913. if (self.downloadTaskDidFinishDownloading) {
  914. NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
  915. if (fileURL) {
  916. delegate.downloadFileURL = fileURL;
  917. NSError *error = nil;
  918. if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
  919. [[NSNotificationCenter defaultCenter] postNotificationName:WFAFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
  920. }
  921. return;
  922. }
  923. }
  924. if (delegate) {
  925. [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
  926. }
  927. }
  928. - (void)URLSession:(NSURLSession *)session
  929. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  930. didWriteData:(int64_t)bytesWritten
  931. totalBytesWritten:(int64_t)totalBytesWritten
  932. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
  933. {
  934. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  935. if (delegate) {
  936. [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  937. }
  938. if (self.downloadTaskDidWriteData) {
  939. self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
  940. }
  941. }
  942. - (void)URLSession:(NSURLSession *)session
  943. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  944. didResumeAtOffset:(int64_t)fileOffset
  945. expectedTotalBytes:(int64_t)expectedTotalBytes
  946. {
  947. WFAFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
  948. if (delegate) {
  949. [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
  950. }
  951. if (self.downloadTaskDidResume) {
  952. self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
  953. }
  954. }
  955. #pragma mark - NSSecureCoding
  956. + (BOOL)supportsSecureCoding {
  957. return YES;
  958. }
  959. - (instancetype)initWithCoder:(NSCoder *)decoder {
  960. NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
  961. self = [self initWithSessionConfiguration:configuration];
  962. if (!self) {
  963. return nil;
  964. }
  965. return self;
  966. }
  967. - (void)encodeWithCoder:(NSCoder *)coder {
  968. [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
  969. }
  970. #pragma mark - NSCopying
  971. - (instancetype)copyWithZone:(NSZone *)zone {
  972. return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
  973. }
  974. @end