FLEXNetworkObserver.m 66 KB

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