FLEXNetworkObserver.m 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592
  1. //
  2. // FLEXNetworkObserver.m
  3. // Derived from:
  4. //
  5. // PDAFNetworkDomainController.m
  6. // PonyDebugger
  7. //
  8. // Created by Mike Lewis on 2/27/12.
  9. //
  10. // Licensed to Square, Inc. under one or more contributor license agreements.
  11. // See the LICENSE file distributed with this work for the terms under
  12. // which Square, Inc. licenses this file to you.
  13. //
  14. #import "FLEXNetworkObserver.h"
  15. #import "FLEXNetworkRecorder.h"
  16. #import "FLEXUtility.h"
  17. #import "NSObject+Reflection.h"
  18. #import "FLEXMethod.h"
  19. #import <objc/runtime.h>
  20. #import <objc/message.h>
  21. #import <dispatch/queue.h>
  22. #include <dlfcn.h>
  23. NSString *const kFLEXNetworkObserverEnabledStateChangedNotification = @"kFLEXNetworkObserverEnabledStateChangedNotification";
  24. static NSString *const kFLEXNetworkObserverEnabledDefaultsKey = @"com.flex.FLEXNetworkObserver.enableOnLaunch";
  25. typedef void (^NSURLSessionAsyncCompletion)(id fileURLOrData, NSURLResponse *response, NSError *error);
  26. typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)(NSURLSession *, id, NSURLSessionAsyncCompletion);
  27. @interface FLEXInternalRequestState : NSObject
  28. @property (nonatomic, copy) NSURLRequest *request;
  29. @property (nonatomic) NSMutableData *dataAccumulator;
  30. @end
  31. @implementation FLEXInternalRequestState
  32. @end
  33. @interface FLEXNetworkObserver (NSURLConnectionHelpers)
  34. - (void)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response delegate:(id<NSURLConnectionDelegate>)delegate;
  35. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response delegate:(id<NSURLConnectionDelegate>)delegate;
  36. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data delegate:(id<NSURLConnectionDelegate>)delegate;
  37. - (void)connectionDidFinishLoading:(NSURLConnection *)connection delegate:(id<NSURLConnectionDelegate>)delegate;
  38. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error delegate:(id<NSURLConnectionDelegate>)delegate;
  39. - (void)connectionWillCancel:(NSURLConnection *)connection;
  40. @end
  41. @interface FLEXNetworkObserver (NSURLSessionTaskHelpers)
  42. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler delegate:(id<NSURLSessionDelegate>)delegate;
  43. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler delegate:(id<NSURLSessionDelegate>)delegate;
  44. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data delegate:(id<NSURLSessionDelegate>)delegate;
  45. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
  46. didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id<NSURLSessionDelegate>)delegate;
  47. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error delegate:(id<NSURLSessionDelegate>)delegate;
  48. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite delegate:(id<NSURLSessionDelegate>)delegate;
  49. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data delegate:(id<NSURLSessionDelegate>)delegate;
  50. - (void)URLSessionTaskWillResume:(NSURLSessionTask *)task;
  51. @end
  52. @interface FLEXNetworkObserver ()
  53. @property (nonatomic) NSMutableDictionary<NSString *, FLEXInternalRequestState *> *requestStatesForRequestIDs;
  54. @property (nonatomic) dispatch_queue_t queue;
  55. @end
  56. @implementation FLEXNetworkObserver
  57. #pragma mark - Public Methods
  58. + (void)setEnabled:(BOOL)enabled {
  59. BOOL previouslyEnabled = [self isEnabled];
  60. [NSUserDefaults.standardUserDefaults setBool:enabled forKey:kFLEXNetworkObserverEnabledDefaultsKey];
  61. if (enabled) {
  62. // Inject if needed. This injection is protected with a dispatch_once, so we're ok calling it multiple times.
  63. // By doing the injection lazily, we keep the impact of the tool lower when this feature isn't enabled.
  64. [self injectIntoAllNSURLConnectionDelegateClasses];
  65. }
  66. if (previouslyEnabled != enabled) {
  67. [NSNotificationCenter.defaultCenter postNotificationName:kFLEXNetworkObserverEnabledStateChangedNotification object:self];
  68. }
  69. }
  70. + (BOOL)isEnabled {
  71. return [[NSUserDefaults.standardUserDefaults objectForKey:kFLEXNetworkObserverEnabledDefaultsKey] boolValue];
  72. }
  73. + (void)load {
  74. // We don't want to do the swizzling from +load because not all the classes may be loaded at this point.
  75. dispatch_async(dispatch_get_main_queue(), ^{
  76. if ([self isEnabled]) {
  77. [self injectIntoAllNSURLConnectionDelegateClasses];
  78. }
  79. });
  80. }
  81. #pragma mark - Statics
  82. + (instancetype)sharedObserver {
  83. static FLEXNetworkObserver *sharedObserver = nil;
  84. static dispatch_once_t onceToken;
  85. dispatch_once(&onceToken, ^{
  86. sharedObserver = [self new];
  87. });
  88. return sharedObserver;
  89. }
  90. + (NSString *)nextRequestID {
  91. return NSUUID.UUID.UUIDString;
  92. }
  93. #pragma mark Delegate Injection Convenience Methods
  94. /// All swizzled delegate methods should make use of this guard.
  95. /// This will prevent duplicated sniffing when the original implementation calls up to a superclass implementation which we've also swizzled.
  96. /// The superclass implementation (and implementations in classes above that) will be executed without interference if called from the original implementation.
  97. + (void)sniffWithoutDuplicationForObject:(NSObject *)object selector:(SEL)selector sniffingBlock:(void (^)(void))sniffingBlock originalImplementationBlock:(void (^)(void))originalImplementationBlock {
  98. // If we don't have an object to detect nested calls on, just run the original implementation and bail.
  99. // This case can happen if someone besides the URL loading system calls the delegate methods directly.
  100. // See https://github.com/Flipboard/FLEX/issues/61 for an example.
  101. if (!object) {
  102. originalImplementationBlock();
  103. return;
  104. }
  105. const void *key = selector;
  106. // Don't run the sniffing block if we're inside a nested call
  107. if (!objc_getAssociatedObject(object, key)) {
  108. sniffingBlock();
  109. }
  110. // Mark that we're calling through to the original so we can detect nested calls
  111. objc_setAssociatedObject(object, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  112. originalImplementationBlock();
  113. objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  114. }
  115. #pragma mark - Delegate Injection
  116. + (void)injectIntoAllNSURLConnectionDelegateClasses {
  117. // Only allow swizzling once.
  118. static dispatch_once_t onceToken;
  119. dispatch_once(&onceToken, ^{
  120. // Swizzle any classes that implement one of these selectors.
  121. const SEL selectors[] = {
  122. @selector(connectionDidFinishLoading:),
  123. @selector(connection:willSendRequest:redirectResponse:),
  124. @selector(connection:didReceiveResponse:),
  125. @selector(connection:didReceiveData:),
  126. @selector(connection:didFailWithError:),
  127. @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:),
  128. @selector(URLSession:dataTask:didReceiveData:),
  129. @selector(URLSession:dataTask:didReceiveResponse:completionHandler:),
  130. @selector(URLSession:task:didCompleteWithError:),
  131. @selector(URLSession:dataTask:didBecomeDownloadTask:),
  132. @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:),
  133. @selector(URLSession:downloadTask:didFinishDownloadingToURL:)
  134. };
  135. const int numSelectors = sizeof(selectors) / sizeof(SEL);
  136. Class *classes = NULL;
  137. int numClasses = objc_getClassList(NULL, 0);
  138. if (numClasses > 0) {
  139. classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
  140. numClasses = objc_getClassList(classes, numClasses);
  141. for (NSInteger classIndex = 0; classIndex < numClasses; ++classIndex) {
  142. Class class = classes[classIndex];
  143. if (class == [FLEXNetworkObserver class]) {
  144. continue;
  145. }
  146. // Use the C API rather than NSObject methods to avoid sending messages
  147. // to classes we're not interested in swizzling, which could result
  148. // in us calling +initialize on potentially uninitialized classes.
  149. // NOTE: calling class_getInstanceMethod() DOES send +initialize
  150. // to the class. That's why we iterate through the method list.
  151. unsigned int methodCount = 0;
  152. Method *methods = class_copyMethodList(class, &methodCount);
  153. BOOL matchingSelectorFound = NO;
  154. for (unsigned int methodIndex = 0; methodIndex < methodCount; methodIndex++) {
  155. for (int selectorIndex = 0; selectorIndex < numSelectors; ++selectorIndex) {
  156. if (method_getName(methods[methodIndex]) == selectors[selectorIndex]) {
  157. [self injectIntoDelegateClass:class];
  158. matchingSelectorFound = YES;
  159. break;
  160. }
  161. }
  162. if (matchingSelectorFound) {
  163. break;
  164. }
  165. }
  166. free(methods);
  167. }
  168. free(classes);
  169. }
  170. [self injectIntoNSURLConnectionCancel];
  171. [self injectIntoNSURLSessionTaskResume];
  172. [self injectIntoNSURLConnectionAsynchronousClassMethod];
  173. [self injectIntoNSURLConnectionSynchronousClassMethod];
  174. [self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods];
  175. [self injectIntoNSURLSessionAsyncUploadTaskMethods];
  176. });
  177. }
  178. + (void)injectIntoDelegateClass:(Class)cls {
  179. // Connections
  180. [self injectWillSendRequestIntoDelegateClass:cls];
  181. [self injectDidReceiveDataIntoDelegateClass:cls];
  182. [self injectDidReceiveResponseIntoDelegateClass:cls];
  183. [self injectDidFinishLoadingIntoDelegateClass:cls];
  184. [self injectDidFailWithErrorIntoDelegateClass:cls];
  185. // Sessions
  186. [self injectTaskWillPerformHTTPRedirectionIntoDelegateClass:cls];
  187. [self injectTaskDidReceiveDataIntoDelegateClass:cls];
  188. [self injectTaskDidReceiveResponseIntoDelegateClass:cls];
  189. [self injectTaskDidCompleteWithErrorIntoDelegateClass:cls];
  190. [self injectRespondsToSelectorIntoDelegateClass:cls];
  191. // Data tasks
  192. [self injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:cls];
  193. // Download tasks
  194. [self injectDownloadTaskDidWriteDataIntoDelegateClass:cls];
  195. [self injectDownloadTaskDidFinishDownloadingIntoDelegateClass:cls];
  196. }
  197. + (void)injectIntoNSURLConnectionCancel {
  198. static dispatch_once_t onceToken;
  199. dispatch_once(&onceToken, ^{
  200. Class class = [NSURLConnection class];
  201. SEL selector = @selector(cancel);
  202. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  203. Method originalCancel = class_getInstanceMethod(class, selector);
  204. void (^swizzleBlock)(NSURLConnection *) = ^(NSURLConnection *slf) {
  205. [FLEXNetworkObserver.sharedObserver connectionWillCancel:slf];
  206. ((void(*)(id, SEL))objc_msgSend)(
  207. slf, swizzledSelector
  208. );
  209. };
  210. IMP implementation = imp_implementationWithBlock(swizzleBlock);
  211. class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalCancel));
  212. Method newCancel = class_getInstanceMethod(class, swizzledSelector);
  213. method_exchangeImplementations(originalCancel, newCancel);
  214. });
  215. }
  216. + (void)injectIntoNSURLSessionTaskResume {
  217. static dispatch_once_t onceToken;
  218. dispatch_once(&onceToken, ^{
  219. // In iOS 7 resume lives in __NSCFLocalSessionTask
  220. // In iOS 8 resume lives in NSURLSessionTask
  221. // In iOS 9 resume lives in __NSCFURLSessionTask
  222. Class baseResumeClass = Nil;
  223. if (![NSProcessInfo.processInfo respondsToSelector:@selector(operatingSystemVersion)]) {
  224. // iOS ... 7
  225. baseResumeClass = NSClassFromString(@"__NSCFLocalSessionTask");
  226. } else {
  227. NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion;
  228. if (majorVersion < 9) {
  229. // iOS 8
  230. baseResumeClass = [NSURLSessionTask class];
  231. } else {
  232. // iOS 9+
  233. baseResumeClass = NSClassFromString(@"__NSCFURLSessionTask");
  234. }
  235. }
  236. // Hook the base implementation of -resume
  237. IMP originalResume = [baseResumeClass instanceMethodForSelector:@selector(resume)];
  238. [self swizzleResumeSelector:@selector(resume) forClass:baseResumeClass];
  239. // *Sigh*
  240. //
  241. // So, multiple versions of AFNetworking 2.5.X swizzle -resume in various and
  242. // short-sighted ways. If you look through the version history from 2.5.0 upwards,
  243. // you'll see a variety of techniques were tried, including taking a private
  244. // subclass of NSURLSessionTask and calling class_addMethod with `originalResume`
  245. // below, so that a duplicate implementation of -resume exists in that class.
  246. //
  247. // This technique in particular is troublesome, because the implementation in
  248. // `baseResumeClass` is never called at all, which means our swizzle is never invoked.
  249. //
  250. // The only solution is a brute-force one: we must loop over the class tree
  251. // below `baseResumeClass` and check for all classes that implement `af_resume`.
  252. // if the IMP corresponding to that method is equal to `originalResume` then we
  253. // swizzle that in addition to swizzling `resume` on `baseResumeClass` above.
  254. //
  255. // However, we only go to the trouble at all if NSSelectorFromString
  256. // can even find an `"af_resume"` selector in the first place.
  257. SEL sel_af_resume = NSSelectorFromString(@"af_resume");
  258. if (sel_af_resume) {
  259. NSMutableArray<Class> *classTree = FLEXGetAllSubclasses(baseResumeClass, NO).mutableCopy;
  260. for (NSInteger i = 0; i < classTree.count; i++) {
  261. [classTree addObjectsFromArray:FLEXGetAllSubclasses(classTree[i], NO)];
  262. }
  263. for (Class current in classTree) {
  264. IMP af_resume = [current instanceMethodForSelector:sel_af_resume];
  265. if (af_resume == originalResume) {
  266. [self swizzleResumeSelector:sel_af_resume forClass:current];
  267. }
  268. }
  269. }
  270. });
  271. }
  272. + (void)swizzleResumeSelector:(SEL)selector forClass:(Class)class {
  273. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  274. Method originalResume = class_getInstanceMethod(class, selector);
  275. IMP implementation = imp_implementationWithBlock(^(NSURLSessionTask *slf) {
  276. // iOS's internal HTTP parser finalization code is mysteriously not thread safe,
  277. // invoking it asynchronously has a chance to cause a `double free` crash.
  278. // This line below will ask for HTTPBody synchronously, make the HTTPParser
  279. // parse the request, and cache them in advance. After that the HTTPParser
  280. // will be finalized. Make sure other threads inspecting the request
  281. // won't trigger a race to finalize the parser.
  282. [slf.currentRequest HTTPBody];
  283. [FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
  284. ((void(*)(id, SEL))objc_msgSend)(
  285. slf, swizzledSelector
  286. );
  287. });
  288. class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalResume));
  289. Method newResume = class_getInstanceMethod(class, swizzledSelector);
  290. method_exchangeImplementations(originalResume, newResume);
  291. }
  292. + (void)injectIntoNSURLConnectionAsynchronousClassMethod {
  293. static dispatch_once_t onceToken;
  294. dispatch_once(&onceToken, ^{
  295. Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
  296. SEL selector = @selector(sendAsynchronousRequest:queue:completionHandler:);
  297. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  298. typedef void (^AsyncCompletion)(
  299. NSURLResponse *response, NSData *data, NSError *error
  300. );
  301. typedef void (^SendAsyncRequestBlock)(
  302. Class, NSURLRequest *, NSOperationQueue *, AsyncCompletion
  303. );
  304. SendAsyncRequestBlock swizzleBlock = ^(Class slf,
  305. NSURLRequest *request,
  306. NSOperationQueue *queue,
  307. AsyncCompletion completion) {
  308. if (FLEXNetworkObserver.isEnabled) {
  309. NSString *requestID = [self nextRequestID];
  310. [FLEXNetworkRecorder.defaultRecorder
  311. recordRequestWillBeSentWithRequestID:requestID
  312. request:request
  313. redirectResponse:nil
  314. ];
  315. NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
  316. [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
  317. AsyncCompletion wrapper = ^(NSURLResponse *response, NSData *data, NSError *error) {
  318. [FLEXNetworkRecorder.defaultRecorder
  319. recordResponseReceivedWithRequestID:requestID
  320. response:response
  321. ];
  322. [FLEXNetworkRecorder.defaultRecorder
  323. recordDataReceivedWithRequestID:requestID
  324. dataLength:data.length
  325. ];
  326. if (error) {
  327. [FLEXNetworkRecorder.defaultRecorder
  328. recordLoadingFailedWithRequestID:requestID
  329. error:error
  330. ];
  331. } else {
  332. [FLEXNetworkRecorder.defaultRecorder
  333. recordLoadingFinishedWithRequestID:requestID
  334. responseBody:data
  335. ];
  336. }
  337. // Call through to the original completion handler
  338. if (completion) {
  339. completion(response, data, error);
  340. }
  341. };
  342. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  343. slf, swizzledSelector, request, queue, wrapper
  344. );
  345. } else {
  346. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  347. slf, swizzledSelector, request, queue, completion
  348. );
  349. }
  350. };
  351. [FLEXUtility replaceImplementationOfKnownSelector:selector
  352. onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
  353. ];
  354. });
  355. }
  356. + (void)injectIntoNSURLConnectionSynchronousClassMethod {
  357. static dispatch_once_t onceToken;
  358. dispatch_once(&onceToken, ^{
  359. Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
  360. SEL selector = @selector(sendSynchronousRequest:returningResponse:error:);
  361. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  362. typedef NSData * (^AsyncCompletion)(Class, NSURLRequest *, NSURLResponse **, NSError **);
  363. AsyncCompletion swizzleBlock = ^NSData *(Class slf,
  364. NSURLRequest *request,
  365. NSURLResponse **response,
  366. NSError **error) {
  367. NSData *data = nil;
  368. if (FLEXNetworkObserver.isEnabled) {
  369. NSString *requestID = [self nextRequestID];
  370. [FLEXNetworkRecorder.defaultRecorder
  371. recordRequestWillBeSentWithRequestID:requestID
  372. request:request
  373. redirectResponse:nil
  374. ];
  375. NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
  376. [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
  377. NSError *temporaryError = nil;
  378. NSURLResponse *temporaryResponse = nil;
  379. data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)(
  380. slf, swizzledSelector, request, &temporaryResponse, &temporaryError
  381. );
  382. [FLEXNetworkRecorder.defaultRecorder
  383. recordResponseReceivedWithRequestID:requestID
  384. response:temporaryResponse
  385. ];
  386. [FLEXNetworkRecorder.defaultRecorder
  387. recordDataReceivedWithRequestID:requestID
  388. dataLength:data.length
  389. ];
  390. if (temporaryError) {
  391. [FLEXNetworkRecorder.defaultRecorder
  392. recordLoadingFailedWithRequestID:requestID
  393. error:temporaryError
  394. ];
  395. } else {
  396. [FLEXNetworkRecorder.defaultRecorder
  397. recordLoadingFinishedWithRequestID:requestID
  398. responseBody:data
  399. ];
  400. }
  401. if (error) {
  402. *error = temporaryError;
  403. }
  404. if (response) {
  405. *response = temporaryResponse;
  406. }
  407. } else {
  408. data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)(
  409. slf, swizzledSelector, request, response, error
  410. );
  411. }
  412. return data;
  413. };
  414. [FLEXUtility replaceImplementationOfKnownSelector:selector
  415. onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
  416. ];
  417. });
  418. }
  419. + (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods {
  420. static dispatch_once_t onceToken;
  421. dispatch_once(&onceToken, ^{
  422. Class class = [NSURLSession class];
  423. // The method signatures here are close enough that
  424. // we can use the same logic to inject into all of them.
  425. const SEL selectors[] = {
  426. @selector(dataTaskWithRequest:completionHandler:),
  427. @selector(dataTaskWithURL:completionHandler:),
  428. @selector(downloadTaskWithRequest:completionHandler:),
  429. @selector(downloadTaskWithResumeData:completionHandler:),
  430. @selector(downloadTaskWithURL:completionHandler:)
  431. };
  432. const int numSelectors = sizeof(selectors) / sizeof(SEL);
  433. for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
  434. SEL selector = selectors[selectorIndex];
  435. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  436. if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
  437. // iOS 7 does not implement these methods on NSURLSession. We actually want to
  438. // swizzle __NSCFURLSession, which we can get from the class of the shared session
  439. class = [NSURLSession.sharedSession class];
  440. }
  441. typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)(
  442. NSURLSession *, id, NSURLSessionAsyncCompletion
  443. );
  444. NSURLSessionNewTaskMethod swizzleBlock = ^NSURLSessionTask *(NSURLSession *slf,
  445. id argument,
  446. NSURLSessionAsyncCompletion completion) {
  447. NSURLSessionTask *task = nil;
  448. // Check if network observing is on and a callback was provided
  449. if (FLEXNetworkObserver.isEnabled && completion) {
  450. NSString *requestID = [self nextRequestID];
  451. NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
  452. // "Hook" the completion block
  453. NSURLSessionAsyncCompletion completionWrapper = [self
  454. asyncCompletionWrapperForRequestID:requestID
  455. mechanism:mechanism
  456. completion:completion
  457. ];
  458. // Call the original method
  459. task = ((id(*)(id, SEL, id, id))objc_msgSend)(
  460. slf, swizzledSelector, argument, completionWrapper
  461. );
  462. [self setRequestID:requestID forConnectionOrTask:task];
  463. } else {
  464. // Network observer disabled or no callback provided,
  465. // just pass through to the original method
  466. task = ((id(*)(id, SEL, id, id))objc_msgSend)(
  467. slf, swizzledSelector, argument, completion
  468. );
  469. }
  470. return task;
  471. };
  472. // Actually swizzle
  473. [FLEXUtility replaceImplementationOfKnownSelector:selector
  474. onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
  475. ];
  476. }
  477. });
  478. }
  479. + (void)injectIntoNSURLSessionAsyncUploadTaskMethods {
  480. static dispatch_once_t onceToken;
  481. dispatch_once(&onceToken, ^{
  482. Class class = [NSURLSession class];
  483. // The method signatures here are close enough that we can use the same logic to inject into both of them.
  484. // Note that they have 3 arguments, so we can't easily combine with the data and download method above.
  485. typedef NSURLSessionUploadTask *(^UploadTaskMethod)(
  486. NSURLSession *, NSURLRequest *, id, NSURLSessionAsyncCompletion
  487. );
  488. const SEL selectors[] = {
  489. @selector(uploadTaskWithRequest:fromData:completionHandler:),
  490. @selector(uploadTaskWithRequest:fromFile:completionHandler:)
  491. };
  492. const int numSelectors = sizeof(selectors) / sizeof(SEL);
  493. for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
  494. SEL selector = selectors[selectorIndex];
  495. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  496. if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
  497. // iOS 7 does not implement these methods on NSURLSession. We actually want to
  498. // swizzle __NSCFURLSession, which we can get from the class of the shared session
  499. class = [NSURLSession.sharedSession class];
  500. }
  501. UploadTaskMethod swizzleBlock = ^NSURLSessionUploadTask *(NSURLSession * slf,
  502. NSURLRequest *request,
  503. id argument,
  504. NSURLSessionAsyncCompletion completion) {
  505. NSURLSessionUploadTask *task = nil;
  506. if (FLEXNetworkObserver.isEnabled && completion) {
  507. NSString *requestID = [self nextRequestID];
  508. NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
  509. NSURLSessionAsyncCompletion completionWrapper = [self
  510. asyncCompletionWrapperForRequestID:requestID
  511. mechanism:mechanism
  512. completion:completion
  513. ];
  514. task = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
  515. slf, swizzledSelector, request, argument, completionWrapper
  516. );
  517. [self setRequestID:requestID forConnectionOrTask:task];
  518. } else {
  519. task = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
  520. slf, swizzledSelector, request, argument, completion
  521. );
  522. }
  523. return task;
  524. };
  525. [FLEXUtility replaceImplementationOfKnownSelector:selector
  526. onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
  527. ];
  528. }
  529. });
  530. }
  531. + (NSString *)mechanismFromClassMethod:(SEL)selector onClass:(Class)class {
  532. return [NSString stringWithFormat:@"+[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)];
  533. }
  534. + (NSURLSessionAsyncCompletion)asyncCompletionWrapperForRequestID:(NSString *)requestID
  535. mechanism:(NSString *)mechanism
  536. completion:(NSURLSessionAsyncCompletion)completion {
  537. NSURLSessionAsyncCompletion completionWrapper = ^(id fileURLOrData, NSURLResponse *response, NSError *error) {
  538. [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
  539. [FLEXNetworkRecorder.defaultRecorder
  540. recordResponseReceivedWithRequestID:requestID
  541. response:response
  542. ];
  543. NSData *data = nil;
  544. if ([fileURLOrData isKindOfClass:[NSURL class]]) {
  545. data = [NSData dataWithContentsOfURL:fileURLOrData];
  546. } else if ([fileURLOrData isKindOfClass:[NSData class]]) {
  547. data = fileURLOrData;
  548. }
  549. [FLEXNetworkRecorder.defaultRecorder
  550. recordDataReceivedWithRequestID:requestID
  551. dataLength:data.length
  552. ];
  553. if (error) {
  554. [FLEXNetworkRecorder.defaultRecorder
  555. recordLoadingFailedWithRequestID:requestID
  556. error:error
  557. ];
  558. } else {
  559. [FLEXNetworkRecorder.defaultRecorder
  560. recordLoadingFinishedWithRequestID:requestID
  561. responseBody:data
  562. ];
  563. }
  564. // Call through to the original completion handler
  565. if (completion) {
  566. completion(fileURLOrData, response, error);
  567. }
  568. };
  569. return completionWrapper;
  570. }
  571. + (void)injectWillSendRequestIntoDelegateClass:(Class)cls {
  572. SEL selector = @selector(connection:willSendRequest:redirectResponse:);
  573. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  574. Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
  575. protocol = protocol ?: @protocol(NSURLConnectionDelegate);
  576. struct objc_method_description methodDescription = protocol_getMethodDescription(
  577. protocol, selector, NO, YES
  578. );
  579. typedef NSURLRequest *(^WillSendRequestBlock)(
  580. id<NSURLConnectionDelegate> slf, NSURLConnection *connection,
  581. NSURLRequest *request, NSURLResponse *response
  582. );
  583. WillSendRequestBlock undefinedBlock = ^NSURLRequest *(id slf,
  584. NSURLConnection *connection,
  585. NSURLRequest *request,
  586. NSURLResponse *response) {
  587. [FLEXNetworkObserver.sharedObserver
  588. connection:connection
  589. willSendRequest:request
  590. redirectResponse:response
  591. delegate:slf
  592. ];
  593. return request;
  594. };
  595. WillSendRequestBlock implementationBlock = ^NSURLRequest *(id slf,
  596. NSURLConnection *connection,
  597. NSURLRequest *request,
  598. NSURLResponse *response) {
  599. __block NSURLRequest *returnValue = nil;
  600. [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
  601. undefinedBlock(slf, connection, request, response);
  602. } originalImplementationBlock:^{
  603. returnValue = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
  604. slf, swizzledSelector, connection, request, response
  605. );
  606. }];
  607. return returnValue;
  608. };
  609. [FLEXUtility replaceImplementationOfSelector:selector
  610. withSelector:swizzledSelector
  611. forClass:cls
  612. withMethodDescription:methodDescription
  613. implementationBlock:implementationBlock
  614. undefinedBlock:undefinedBlock
  615. ];
  616. }
  617. + (void)injectDidReceiveResponseIntoDelegateClass:(Class)cls {
  618. SEL selector = @selector(connection:didReceiveResponse:);
  619. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  620. Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
  621. protocol = protocol ?: @protocol(NSURLConnectionDelegate);
  622. struct objc_method_description description = protocol_getMethodDescription(
  623. protocol, selector, NO, YES
  624. );
  625. typedef void (^DidReceiveResponseBlock)(
  626. id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSURLResponse *response
  627. );
  628. DidReceiveResponseBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
  629. NSURLConnection *connection,
  630. NSURLResponse *response) {
  631. [FLEXNetworkObserver.sharedObserver connection:connection
  632. didReceiveResponse:response delegate:slf
  633. ];
  634. };
  635. DidReceiveResponseBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
  636. NSURLConnection *connection,
  637. NSURLResponse *response) {
  638. [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
  639. undefinedBlock(slf, connection, response);
  640. } originalImplementationBlock:^{
  641. ((void(*)(id, SEL, id, id))objc_msgSend)(
  642. slf, swizzledSelector, connection, response
  643. );
  644. }];
  645. };
  646. [FLEXUtility replaceImplementationOfSelector:selector
  647. withSelector:swizzledSelector
  648. forClass:cls
  649. withMethodDescription:description
  650. implementationBlock:implementationBlock
  651. undefinedBlock:undefinedBlock
  652. ];
  653. }
  654. + (void)injectDidReceiveDataIntoDelegateClass:(Class)cls {
  655. SEL selector = @selector(connection:didReceiveData:);
  656. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  657. Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
  658. protocol = protocol ?: @protocol(NSURLConnectionDelegate);
  659. struct objc_method_description description = protocol_getMethodDescription(
  660. protocol, selector, NO, YES
  661. );
  662. typedef void (^DidReceiveDataBlock)(
  663. id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSData *data
  664. );
  665. DidReceiveDataBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
  666. NSURLConnection *connection,
  667. NSData *data) {
  668. [FLEXNetworkObserver.sharedObserver connection:connection
  669. didReceiveData:data delegate:slf
  670. ];
  671. };
  672. DidReceiveDataBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
  673. NSURLConnection *connection,
  674. NSData *data) {
  675. [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
  676. undefinedBlock(slf, connection, data);
  677. } originalImplementationBlock:^{
  678. ((void(*)(id, SEL, id, id))objc_msgSend)(
  679. slf, swizzledSelector, connection, data
  680. );
  681. }];
  682. };
  683. [FLEXUtility replaceImplementationOfSelector:selector
  684. withSelector:swizzledSelector
  685. forClass:cls
  686. withMethodDescription:description
  687. implementationBlock:implementationBlock
  688. undefinedBlock:undefinedBlock
  689. ];
  690. }
  691. + (void)injectDidFinishLoadingIntoDelegateClass:(Class)cls {
  692. SEL selector = @selector(connectionDidFinishLoading:);
  693. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  694. Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
  695. protocol = protocol ?: @protocol(NSURLConnectionDelegate);
  696. struct objc_method_description description = protocol_getMethodDescription(
  697. protocol, selector, NO, YES
  698. );
  699. typedef void (^FinishLoadingBlock)(id<NSURLConnectionDelegate> slf, NSURLConnection *connection);
  700. FinishLoadingBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf, NSURLConnection *connection) {
  701. [FLEXNetworkObserver.sharedObserver connectionDidFinishLoading:connection delegate:slf];
  702. };
  703. FinishLoadingBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf, NSURLConnection *connection) {
  704. [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
  705. undefinedBlock(slf, connection);
  706. } originalImplementationBlock:^{
  707. ((void(*)(id, SEL, id))objc_msgSend)(
  708. slf, swizzledSelector, connection
  709. );
  710. }];
  711. };
  712. [FLEXUtility replaceImplementationOfSelector:selector
  713. withSelector:swizzledSelector forClass:cls
  714. withMethodDescription:description
  715. implementationBlock:implementationBlock
  716. undefinedBlock:undefinedBlock
  717. ];
  718. }
  719. + (void)injectDidFailWithErrorIntoDelegateClass:(Class)cls {
  720. SEL selector = @selector(connection:didFailWithError:);
  721. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  722. struct objc_method_description description = protocol_getMethodDescription(
  723. @protocol(NSURLConnectionDelegate), selector, NO, YES
  724. );
  725. typedef void (^DidFailWithErrorBlock)(
  726. id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSError *error
  727. );
  728. DidFailWithErrorBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
  729. NSURLConnection *connection,
  730. NSError *error) {
  731. [FLEXNetworkObserver.sharedObserver connection:connection
  732. didFailWithError:error delegate:slf
  733. ];
  734. };
  735. DidFailWithErrorBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
  736. NSURLConnection *connection,
  737. NSError *error) {
  738. [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
  739. undefinedBlock(slf, connection, error);
  740. } originalImplementationBlock:^{
  741. ((void(*)(id, SEL, id, id))objc_msgSend)(
  742. slf, swizzledSelector, connection, error
  743. );
  744. }];
  745. };
  746. [FLEXUtility replaceImplementationOfSelector:selector
  747. withSelector:swizzledSelector forClass:cls
  748. withMethodDescription:description
  749. implementationBlock:implementationBlock
  750. undefinedBlock:undefinedBlock
  751. ];
  752. }
  753. + (void)injectTaskWillPerformHTTPRedirectionIntoDelegateClass:(Class)cls {
  754. SEL selector = @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:);
  755. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  756. struct objc_method_description description = protocol_getMethodDescription(
  757. @protocol(NSURLSessionTaskDelegate), selector, NO, YES
  758. );
  759. typedef void (^HTTPRedirectionBlock)(id<NSURLSessionTaskDelegate> slf,
  760. NSURLSession *session,
  761. NSURLSessionTask *task,
  762. NSHTTPURLResponse *response,
  763. NSURLRequest *newRequest,
  764. void(^completionHandler)(NSURLRequest *));
  765. HTTPRedirectionBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
  766. NSURLSession *session,
  767. NSURLSessionTask *task,
  768. NSHTTPURLResponse *response,
  769. NSURLRequest *newRequest,
  770. void(^completionHandler)(NSURLRequest *)) {
  771. [FLEXNetworkObserver.sharedObserver
  772. URLSession:session task:task
  773. willPerformHTTPRedirection:response
  774. newRequest:newRequest
  775. completionHandler:completionHandler
  776. delegate:slf
  777. ];
  778. completionHandler(newRequest);
  779. };
  780. HTTPRedirectionBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
  781. NSURLSession *session,
  782. NSURLSessionTask *task,
  783. NSHTTPURLResponse *response,
  784. NSURLRequest *newRequest,
  785. void(^completionHandler)(NSURLRequest *)) {
  786. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  787. [FLEXNetworkObserver.sharedObserver
  788. URLSession:session task:task
  789. willPerformHTTPRedirection:response
  790. newRequest:newRequest
  791. completionHandler:completionHandler
  792. delegate:slf
  793. ];
  794. } originalImplementationBlock:^{
  795. ((id(*)(id, SEL, id, id, id, id, void(^)(NSURLRequest *)))objc_msgSend)(
  796. slf, swizzledSelector, session, task, response, newRequest, completionHandler
  797. );
  798. }];
  799. };
  800. [FLEXUtility replaceImplementationOfSelector:selector
  801. withSelector:swizzledSelector
  802. forClass:cls
  803. withMethodDescription:description
  804. implementationBlock:implementationBlock
  805. undefinedBlock:undefinedBlock
  806. ];
  807. }
  808. + (void)injectTaskDidReceiveDataIntoDelegateClass:(Class)cls {
  809. SEL selector = @selector(URLSession:dataTask:didReceiveData:);
  810. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  811. struct objc_method_description description = protocol_getMethodDescription(
  812. @protocol(NSURLSessionDataDelegate), selector, NO, YES
  813. );
  814. typedef void (^DidReceiveDataBlock)(id<NSURLSessionDataDelegate> slf,
  815. NSURLSession *session,
  816. NSURLSessionDataTask *dataTask,
  817. NSData *data);
  818. DidReceiveDataBlock undefinedBlock = ^(id<NSURLSessionDataDelegate> slf,
  819. NSURLSession *session,
  820. NSURLSessionDataTask *dataTask,
  821. NSData *data) {
  822. [FLEXNetworkObserver.sharedObserver URLSession:session
  823. dataTask:dataTask didReceiveData:data delegate:slf
  824. ];
  825. };
  826. DidReceiveDataBlock implementationBlock = ^(id<NSURLSessionDataDelegate> slf,
  827. NSURLSession *session,
  828. NSURLSessionDataTask *dataTask,
  829. NSData *data) {
  830. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  831. undefinedBlock(slf, session, dataTask, data);
  832. } originalImplementationBlock:^{
  833. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  834. slf, swizzledSelector, session, dataTask, data
  835. );
  836. }];
  837. };
  838. [FLEXUtility replaceImplementationOfSelector:selector
  839. withSelector:swizzledSelector
  840. forClass:cls
  841. withMethodDescription:description
  842. implementationBlock:implementationBlock
  843. undefinedBlock:undefinedBlock
  844. ];
  845. }
  846. + (void)injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:(Class)cls {
  847. SEL selector = @selector(URLSession:dataTask:didBecomeDownloadTask:);
  848. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  849. struct objc_method_description description = protocol_getMethodDescription(
  850. @protocol(NSURLSessionDataDelegate), selector, NO, YES
  851. );
  852. typedef void (^DidBecomeDownloadTaskBlock)(id<NSURLSessionDataDelegate> slf,
  853. NSURLSession *session,
  854. NSURLSessionDataTask *dataTask,
  855. NSURLSessionDownloadTask *downloadTask);
  856. DidBecomeDownloadTaskBlock undefinedBlock = ^(id<NSURLSessionDataDelegate> slf,
  857. NSURLSession *session,
  858. NSURLSessionDataTask *dataTask,
  859. NSURLSessionDownloadTask *downloadTask) {
  860. [FLEXNetworkObserver.sharedObserver URLSession:session
  861. dataTask:dataTask didBecomeDownloadTask:downloadTask delegate:slf
  862. ];
  863. };
  864. DidBecomeDownloadTaskBlock implementationBlock = ^(id<NSURLSessionDataDelegate> slf,
  865. NSURLSession *session,
  866. NSURLSessionDataTask *dataTask,
  867. NSURLSessionDownloadTask *downloadTask) {
  868. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  869. undefinedBlock(slf, session, dataTask, downloadTask);
  870. } originalImplementationBlock:^{
  871. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  872. slf, swizzledSelector, session, dataTask, downloadTask
  873. );
  874. }];
  875. };
  876. [FLEXUtility replaceImplementationOfSelector:selector
  877. withSelector:swizzledSelector
  878. forClass:cls
  879. withMethodDescription:description
  880. implementationBlock:implementationBlock
  881. undefinedBlock:undefinedBlock
  882. ];
  883. }
  884. + (void)injectTaskDidReceiveResponseIntoDelegateClass:(Class)cls {
  885. SEL selector = @selector(URLSession:dataTask:didReceiveResponse:completionHandler:);
  886. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  887. struct objc_method_description description = protocol_getMethodDescription(
  888. @protocol(NSURLSessionDataDelegate), selector, NO, YES
  889. );
  890. typedef void (^DidReceiveResponseBlock)(id<NSURLSessionDelegate> slf,
  891. NSURLSession *session,
  892. NSURLSessionDataTask *dataTask,
  893. NSURLResponse *response,
  894. void(^completion)(NSURLSessionResponseDisposition));
  895. DidReceiveResponseBlock undefinedBlock = ^(id<NSURLSessionDelegate> slf,
  896. NSURLSession *session,
  897. NSURLSessionDataTask *dataTask,
  898. NSURLResponse *response,
  899. void(^completion)(NSURLSessionResponseDisposition)) {
  900. [FLEXNetworkObserver.sharedObserver
  901. URLSession:session
  902. dataTask:dataTask
  903. didReceiveResponse:response
  904. completionHandler:completion
  905. delegate:slf
  906. ];
  907. completion(NSURLSessionResponseAllow);
  908. };
  909. DidReceiveResponseBlock implementationBlock = ^(id<NSURLSessionDelegate> slf,
  910. NSURLSession *session,
  911. NSURLSessionDataTask *dataTask,
  912. NSURLResponse *response,
  913. void(^completion)(NSURLSessionResponseDisposition )) {
  914. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  915. [FLEXNetworkObserver.sharedObserver
  916. URLSession:session
  917. dataTask:dataTask
  918. didReceiveResponse:response
  919. completionHandler:completion
  920. delegate:slf
  921. ];
  922. } originalImplementationBlock:^{
  923. ((void(*)(id, SEL, id, id, id, void(^)(NSURLSessionResponseDisposition)))objc_msgSend)(
  924. slf, swizzledSelector, session, dataTask, response, completion
  925. );
  926. }];
  927. };
  928. [FLEXUtility replaceImplementationOfSelector:selector
  929. withSelector:swizzledSelector
  930. forClass:cls
  931. withMethodDescription:description
  932. implementationBlock:implementationBlock
  933. undefinedBlock:undefinedBlock
  934. ];
  935. }
  936. + (void)injectTaskDidCompleteWithErrorIntoDelegateClass:(Class)cls {
  937. SEL selector = @selector(URLSession:task:didCompleteWithError:);
  938. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  939. struct objc_method_description description = protocol_getMethodDescription(
  940. @protocol(NSURLSessionDataDelegate), selector, NO, YES
  941. );
  942. typedef void (^DidCompleteWithErrorBlock)(id<NSURLSessionTaskDelegate> slf,
  943. NSURLSession *session,
  944. NSURLSessionTask *task,
  945. NSError *error);
  946. DidCompleteWithErrorBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
  947. NSURLSession *session,
  948. NSURLSessionTask *task,
  949. NSError *error) {
  950. [FLEXNetworkObserver.sharedObserver URLSession:session
  951. task:task didCompleteWithError:error delegate:slf
  952. ];
  953. };
  954. DidCompleteWithErrorBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
  955. NSURLSession *session,
  956. NSURLSessionTask *task,
  957. NSError *error) {
  958. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  959. undefinedBlock(slf, session, task, error);
  960. } originalImplementationBlock:^{
  961. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  962. slf, swizzledSelector, session, task, error
  963. );
  964. }];
  965. };
  966. [FLEXUtility replaceImplementationOfSelector:selector
  967. withSelector:swizzledSelector
  968. forClass:cls
  969. withMethodDescription:description
  970. implementationBlock:implementationBlock
  971. undefinedBlock:undefinedBlock
  972. ];
  973. }
  974. // Used for overriding AFNetworking behavior
  975. + (void)injectRespondsToSelectorIntoDelegateClass:(Class)cls {
  976. SEL selector = @selector(respondsToSelector:);
  977. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  978. //Protocol *protocol = @protocol(NSURLSessionTaskDelegate);
  979. Method method = class_getInstanceMethod(cls, selector);
  980. struct objc_method_description methodDescription = *method_getDescription(method);
  981. typedef BOOL (^RespondsToSelectorImpl)(id self, SEL sel);
  982. RespondsToSelectorImpl undefinedBlock = ^(id slf, SEL sel) {
  983. return YES;
  984. };
  985. RespondsToSelectorImpl implementationBlock = ^(id<NSURLSessionTaskDelegate> slf, SEL sel) {
  986. if (sel == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
  987. return undefinedBlock(slf, sel);
  988. }
  989. return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(slf, swizzledSelector, sel);
  990. };
  991. [FLEXUtility replaceImplementationOfSelector:selector
  992. withSelector:swizzledSelector
  993. forClass:cls
  994. withMethodDescription:methodDescription
  995. implementationBlock:implementationBlock
  996. undefinedBlock:undefinedBlock
  997. ];
  998. }
  999. + (void)injectDownloadTaskDidFinishDownloadingIntoDelegateClass:(Class)cls {
  1000. SEL selector = @selector(URLSession:downloadTask:didFinishDownloadingToURL:);
  1001. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  1002. struct objc_method_description description = protocol_getMethodDescription(
  1003. @protocol(NSURLSessionDownloadDelegate), selector, NO, YES
  1004. );
  1005. typedef void (^DidFinishDownloadingBlock)(id<NSURLSessionTaskDelegate> slf,
  1006. NSURLSession *session,
  1007. NSURLSessionDownloadTask *task,
  1008. NSURL *location);
  1009. DidFinishDownloadingBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
  1010. NSURLSession *session,
  1011. NSURLSessionDownloadTask *task,
  1012. NSURL *location) {
  1013. NSData *data = [NSData dataWithContentsOfFile:location.relativePath];
  1014. [FLEXNetworkObserver.sharedObserver URLSession:session
  1015. task:task didFinishDownloadingToURL:location data:data delegate:slf
  1016. ];
  1017. };
  1018. DidFinishDownloadingBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
  1019. NSURLSession *session,
  1020. NSURLSessionDownloadTask *task,
  1021. NSURL *location) {
  1022. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  1023. undefinedBlock(slf, session, task, location);
  1024. } originalImplementationBlock:^{
  1025. ((void(*)(id, SEL, id, id, id))objc_msgSend)(
  1026. slf, swizzledSelector, session, task, location
  1027. );
  1028. }];
  1029. };
  1030. [FLEXUtility replaceImplementationOfSelector:selector
  1031. withSelector:swizzledSelector
  1032. forClass:cls
  1033. withMethodDescription:description
  1034. implementationBlock:implementationBlock
  1035. undefinedBlock:undefinedBlock
  1036. ];
  1037. }
  1038. + (void)injectDownloadTaskDidWriteDataIntoDelegateClass:(Class)cls {
  1039. SEL selector = @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:);
  1040. SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
  1041. struct objc_method_description description = protocol_getMethodDescription(
  1042. @protocol(NSURLSessionDownloadDelegate), selector, NO, YES
  1043. );
  1044. typedef void (^DidWriteDataBlock)(id<NSURLSessionTaskDelegate> slf,
  1045. NSURLSession *session,
  1046. NSURLSessionDownloadTask *task,
  1047. int64_t bytesWritten,
  1048. int64_t totalBytesWritten,
  1049. int64_t totalBytesExpectedToWrite);
  1050. DidWriteDataBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
  1051. NSURLSession *session,
  1052. NSURLSessionDownloadTask *task,
  1053. int64_t bytesWritten,
  1054. int64_t totalBytesWritten,
  1055. int64_t totalBytesExpectedToWrite) {
  1056. [FLEXNetworkObserver.sharedObserver URLSession:session
  1057. downloadTask:task didWriteData:bytesWritten
  1058. totalBytesWritten:totalBytesWritten
  1059. totalBytesExpectedToWrite:totalBytesExpectedToWrite
  1060. delegate:slf
  1061. ];
  1062. };
  1063. DidWriteDataBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
  1064. NSURLSession *session,
  1065. NSURLSessionDownloadTask *task,
  1066. int64_t bytesWritten,
  1067. int64_t totalBytesWritten,
  1068. int64_t totalBytesExpectedToWrite) {
  1069. [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
  1070. undefinedBlock(
  1071. slf, session, task, bytesWritten,
  1072. totalBytesWritten, totalBytesExpectedToWrite
  1073. );
  1074. } originalImplementationBlock:^{
  1075. ((void(*)(id, SEL, id, id, int64_t, int64_t, int64_t))objc_msgSend)(
  1076. slf, swizzledSelector, session, task, bytesWritten,
  1077. totalBytesWritten, totalBytesExpectedToWrite
  1078. );
  1079. }];
  1080. };
  1081. [FLEXUtility replaceImplementationOfSelector:selector
  1082. withSelector:swizzledSelector
  1083. forClass:cls
  1084. withMethodDescription:description
  1085. implementationBlock:implementationBlock
  1086. undefinedBlock:undefinedBlock
  1087. ];
  1088. }
  1089. static char const * const kFLEXRequestIDKey = "kFLEXRequestIDKey";
  1090. + (NSString *)requestIDForConnectionOrTask:(id)connectionOrTask {
  1091. NSString *requestID = objc_getAssociatedObject(connectionOrTask, kFLEXRequestIDKey);
  1092. if (!requestID) {
  1093. requestID = [self nextRequestID];
  1094. [self setRequestID:requestID forConnectionOrTask:connectionOrTask];
  1095. }
  1096. return requestID;
  1097. }
  1098. + (void)setRequestID:(NSString *)requestID forConnectionOrTask:(id)connectionOrTask {
  1099. objc_setAssociatedObject(
  1100. connectionOrTask, kFLEXRequestIDKey, requestID, OBJC_ASSOCIATION_RETAIN_NONATOMIC
  1101. );
  1102. }
  1103. #pragma mark - Initialization
  1104. - (id)init {
  1105. self = [super init];
  1106. if (self) {
  1107. self.requestStatesForRequestIDs = [NSMutableDictionary new];
  1108. self.queue = dispatch_queue_create(
  1109. "com.flex.FLEXNetworkObserver", DISPATCH_QUEUE_SERIAL
  1110. );
  1111. }
  1112. return self;
  1113. }
  1114. #pragma mark - Private Methods
  1115. - (void)performBlock:(dispatch_block_t)block {
  1116. if ([[self class] isEnabled]) {
  1117. dispatch_async(_queue, block);
  1118. }
  1119. }
  1120. - (FLEXInternalRequestState *)requestStateForRequestID:(NSString *)requestID {
  1121. FLEXInternalRequestState *requestState = self.requestStatesForRequestIDs[requestID];
  1122. if (!requestState) {
  1123. requestState = [FLEXInternalRequestState new];
  1124. [self.requestStatesForRequestIDs setObject:requestState forKey:requestID];
  1125. }
  1126. return requestState;
  1127. }
  1128. - (void)removeRequestStateForRequestID:(NSString *)requestID {
  1129. [self.requestStatesForRequestIDs removeObjectForKey:requestID];
  1130. }
  1131. @end
  1132. @implementation FLEXNetworkObserver (NSURLConnectionHelpers)
  1133. - (void)connection:(NSURLConnection *)connection
  1134. willSendRequest:(NSURLRequest *)request
  1135. redirectResponse:(NSURLResponse *)response
  1136. delegate:(id<NSURLConnectionDelegate>)delegate {
  1137. [self performBlock:^{
  1138. NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
  1139. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1140. requestState.request = request;
  1141. [FLEXNetworkRecorder.defaultRecorder
  1142. recordRequestWillBeSentWithRequestID:requestID
  1143. request:request
  1144. redirectResponse:response
  1145. ];
  1146. NSString *mechanism = [NSString stringWithFormat:
  1147. @"NSURLConnection (delegate: %@)", [delegate class]
  1148. ];
  1149. [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
  1150. }];
  1151. }
  1152. - (void)connection:(NSURLConnection *)connection
  1153. didReceiveResponse:(NSURLResponse *)response
  1154. delegate:(id<NSURLConnectionDelegate>)delegate {
  1155. [self performBlock:^{
  1156. NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
  1157. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1158. requestState.dataAccumulator = [NSMutableData new];
  1159. [FLEXNetworkRecorder.defaultRecorder
  1160. recordResponseReceivedWithRequestID:requestID
  1161. response:response
  1162. ];
  1163. }];
  1164. }
  1165. - (void)connection:(NSURLConnection *)connection
  1166. didReceiveData:(NSData *)data
  1167. delegate:(id<NSURLConnectionDelegate>)delegate {
  1168. // Just to be safe since we're doing this async
  1169. data = [data copy];
  1170. [self performBlock:^{
  1171. NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
  1172. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1173. [requestState.dataAccumulator appendData:data];
  1174. [FLEXNetworkRecorder.defaultRecorder
  1175. recordDataReceivedWithRequestID:requestID
  1176. dataLength:data.length
  1177. ];
  1178. }];
  1179. }
  1180. - (void)connectionDidFinishLoading:(NSURLConnection *)connection
  1181. delegate:(id<NSURLConnectionDelegate>)delegate {
  1182. [self performBlock:^{
  1183. NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
  1184. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1185. [FLEXNetworkRecorder.defaultRecorder
  1186. recordLoadingFinishedWithRequestID:requestID
  1187. responseBody:requestState.dataAccumulator
  1188. ];
  1189. [self removeRequestStateForRequestID:requestID];
  1190. }];
  1191. }
  1192. - (void)connection:(NSURLConnection *)connection
  1193. didFailWithError:(NSError *)error
  1194. delegate:(id<NSURLConnectionDelegate>)delegate {
  1195. [self performBlock:^{
  1196. NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
  1197. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1198. // Cancellations can occur prior to the willSendRequest:...
  1199. // NSURLConnection delegate call. These are pretty common
  1200. // and clutter up the logs. Only record the failure if the
  1201. // recorder already knows about the request through willSendRequest:...
  1202. if (requestState.request) {
  1203. [FLEXNetworkRecorder.defaultRecorder
  1204. recordLoadingFailedWithRequestID:requestID error:error
  1205. ];
  1206. }
  1207. [self removeRequestStateForRequestID:requestID];
  1208. }];
  1209. }
  1210. - (void)connectionWillCancel:(NSURLConnection *)connection {
  1211. [self performBlock:^{
  1212. // Mimic the behavior of NSURLSession which is to create an error on cancellation.
  1213. NSDictionary<NSString *, id> *userInfo = @{ NSLocalizedDescriptionKey : @"cancelled" };
  1214. NSError *error = [NSError errorWithDomain:NSURLErrorDomain
  1215. code:NSURLErrorCancelled userInfo:userInfo
  1216. ];
  1217. [self connection:connection didFailWithError:error delegate:nil];
  1218. }];
  1219. }
  1220. @end
  1221. @implementation FLEXNetworkObserver (NSURLSessionTaskHelpers)
  1222. - (void)URLSession:(NSURLSession *)session
  1223. task:(NSURLSessionTask *)task
  1224. willPerformHTTPRedirection:(NSHTTPURLResponse *)response
  1225. newRequest:(NSURLRequest *)request
  1226. completionHandler:(void (^)(NSURLRequest *))completionHandler
  1227. delegate:(id<NSURLSessionDelegate>)delegate {
  1228. [self performBlock:^{
  1229. NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
  1230. [FLEXNetworkRecorder.defaultRecorder
  1231. recordRequestWillBeSentWithRequestID:requestID
  1232. request:request
  1233. redirectResponse:response
  1234. ];
  1235. }];
  1236. }
  1237. - (void)URLSession:(NSURLSession *)session
  1238. dataTask:(NSURLSessionDataTask *)dataTask
  1239. didReceiveResponse:(NSURLResponse *)response
  1240. completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  1241. delegate:(id<NSURLSessionDelegate>)delegate {
  1242. [self performBlock:^{
  1243. NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
  1244. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1245. requestState.dataAccumulator = [NSMutableData new];
  1246. NSString *requestMechanism = [NSString stringWithFormat:
  1247. @"NSURLSessionDataTask (delegate: %@)", [delegate class]
  1248. ];
  1249. [FLEXNetworkRecorder.defaultRecorder
  1250. recordMechanism:requestMechanism
  1251. forRequestID:requestID
  1252. ];
  1253. [FLEXNetworkRecorder.defaultRecorder
  1254. recordResponseReceivedWithRequestID:requestID
  1255. response:response
  1256. ];
  1257. }];
  1258. }
  1259. - (void)URLSession:(NSURLSession *)session
  1260. dataTask:(NSURLSessionDataTask *)dataTask
  1261. didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  1262. delegate:(id<NSURLSessionDelegate>)delegate {
  1263. [self performBlock:^{
  1264. // By setting the request ID of the download task to match the data task,
  1265. // it can pick up where the data task left off.
  1266. NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
  1267. [[self class] setRequestID:requestID forConnectionOrTask:downloadTask];
  1268. }];
  1269. }
  1270. - (void)URLSession:(NSURLSession *)session
  1271. dataTask:(NSURLSessionDataTask *)dataTask
  1272. didReceiveData:(NSData *)data
  1273. delegate:(id<NSURLSessionDelegate>)delegate {
  1274. // Just to be safe since we're doing this async
  1275. data = [data copy];
  1276. [self performBlock:^{
  1277. NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
  1278. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1279. [requestState.dataAccumulator appendData:data];
  1280. [FLEXNetworkRecorder.defaultRecorder
  1281. recordDataReceivedWithRequestID:requestID
  1282. dataLength:data.length
  1283. ];
  1284. }];
  1285. }
  1286. - (void)URLSession:(NSURLSession *)session
  1287. task:(NSURLSessionTask *)task
  1288. didCompleteWithError:(NSError *)error
  1289. delegate:(id<NSURLSessionDelegate>)delegate {
  1290. [self performBlock:^{
  1291. NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
  1292. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1293. if (error) {
  1294. [FLEXNetworkRecorder.defaultRecorder
  1295. recordLoadingFailedWithRequestID:requestID error:error
  1296. ];
  1297. } else {
  1298. [FLEXNetworkRecorder.defaultRecorder
  1299. recordLoadingFinishedWithRequestID:requestID
  1300. responseBody:requestState.dataAccumulator
  1301. ];
  1302. }
  1303. [self removeRequestStateForRequestID:requestID];
  1304. }];
  1305. }
  1306. - (void)URLSession:(NSURLSession *)session
  1307. downloadTask:(NSURLSessionDownloadTask *)downloadTask
  1308. didWriteData:(int64_t)bytesWritten
  1309. totalBytesWritten:(int64_t)totalBytesWritten
  1310. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
  1311. delegate:(id<NSURLSessionDelegate>)delegate {
  1312. [self performBlock:^{
  1313. NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask];
  1314. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1315. if (!requestState.dataAccumulator) {
  1316. requestState.dataAccumulator = [NSMutableData new];
  1317. [FLEXNetworkRecorder.defaultRecorder
  1318. recordResponseReceivedWithRequestID:requestID
  1319. response:downloadTask.response
  1320. ];
  1321. NSString *requestMechanism = [NSString stringWithFormat:
  1322. @"NSURLSessionDownloadTask (delegate: %@)", [delegate class]
  1323. ];
  1324. [FLEXNetworkRecorder.defaultRecorder
  1325. recordMechanism:requestMechanism
  1326. forRequestID:requestID
  1327. ];
  1328. }
  1329. [FLEXNetworkRecorder.defaultRecorder
  1330. recordDataReceivedWithRequestID:requestID
  1331. dataLength:bytesWritten
  1332. ];
  1333. }];
  1334. }
  1335. - (void)URLSession:(NSURLSession *)session
  1336. task:(NSURLSessionDownloadTask *)downloadTask
  1337. didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data
  1338. delegate:(id<NSURLSessionDelegate>)delegate {
  1339. data = [data copy];
  1340. [self performBlock:^{
  1341. NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask];
  1342. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1343. [requestState.dataAccumulator appendData:data];
  1344. }];
  1345. }
  1346. - (void)URLSessionTaskWillResume:(NSURLSessionTask *)task {
  1347. // Since resume can be called multiple times on the same task, only treat the first resume as
  1348. // the equivalent to connection:willSendRequest:...
  1349. [self performBlock:^{
  1350. NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
  1351. FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
  1352. if (!requestState.request) {
  1353. requestState.request = task.currentRequest;
  1354. [FLEXNetworkRecorder.defaultRecorder
  1355. recordRequestWillBeSentWithRequestID:requestID
  1356. request:task.currentRequest
  1357. redirectResponse:nil
  1358. ];
  1359. }
  1360. }];
  1361. }
  1362. @end