FPRNSURLSessionInstrument.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /** NSURLSession is a class cluster and the type of class you get back from the various
  15. * initialization methods might not actually be NSURLSession. Inside those methods, this class
  16. * keeps track of seen NSURLSession subclasses and lazily swizzles them if they've not been seen.
  17. * Consequently, swizzling needs to occur on a serial queue for thread safety.
  18. */
  19. #import "FirebasePerformance/Sources/Instrumentation/Network/FPRNSURLSessionInstrument.h"
  20. #import "FirebasePerformance/Sources/Instrumentation/Network/FPRNSURLSessionInstrument_Private.h"
  21. #import "FirebasePerformance/Sources/Common/FPRDiagnostics.h"
  22. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
  23. #import "FirebasePerformance/Sources/Instrumentation/FPRClassInstrumentor.h"
  24. #import "FirebasePerformance/Sources/Instrumentation/FPRInstrument_Private.h"
  25. #import "FirebasePerformance/Sources/Instrumentation/FPRNetworkTrace.h"
  26. #import "FirebasePerformance/Sources/Instrumentation/FPRProxyObjectHelper.h"
  27. #import "FirebasePerformance/Sources/Instrumentation/FPRSelectorInstrumentor.h"
  28. #import "FirebasePerformance/Sources/Instrumentation/Network/Delegates/FPRNSURLSessionDelegate.h"
  29. #import "FirebasePerformance/Sources/Instrumentation/Network/FPRNetworkInstrumentHelpers.h"
  30. #import <GoogleUtilities/GULObjectSwizzler.h>
  31. // Declared for use in instrumentation functions below.
  32. @interface FPRNSURLSessionInstrument ()
  33. /** Registers an instrumentor for an NSURLSession subclass if it hasn't yet been instrumented.
  34. *
  35. * @param aClass The class we wish to instrument.
  36. */
  37. - (void)registerInstrumentorForClass:(Class)aClass;
  38. /** Registers an instrumentor for an NSURLSession proxy object if it hasn't yet been instrumented.
  39. *
  40. * @param proxy The proxy object we wish to instrument.
  41. */
  42. - (void)registerProxyObject:(id)proxy;
  43. @end
  44. /** Returns the dispatch queue for all instrumentation to occur on. */
  45. static dispatch_queue_t GetInstrumentationQueue() {
  46. static dispatch_queue_t queue = nil;
  47. static dispatch_once_t token = 0;
  48. dispatch_once(&token, ^{
  49. queue =
  50. dispatch_queue_create("com.google.FPRNSURLSessionInstrumentation", DISPATCH_QUEUE_SERIAL);
  51. });
  52. return queue;
  53. }
  54. // This completion handler type is commonly used throughout NSURLSession.
  55. typedef void (^FPRDataTaskCompletionHandler)(NSData *_Nullable,
  56. NSURLResponse *_Nullable,
  57. NSError *_Nullable);
  58. typedef void (^FPRDownloadTaskCompletionHandler)(NSURL *_Nullable location,
  59. NSURLResponse *_Nullable response,
  60. NSError *_Nullable error);
  61. #pragma mark - Instrumentation Functions
  62. /** Wraps +sharedSession.
  63. *
  64. * @param instrument The FPRNSURLSessionInstrument instance.
  65. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  66. */
  67. FOUNDATION_STATIC_INLINE
  68. void InstrumentSharedSession(FPRNSURLSessionInstrument *instrument,
  69. FPRClassInstrumentor *instrumentor) {
  70. SEL selector = @selector(sharedSession);
  71. Class instrumentedClass = instrumentor.instrumentedClass;
  72. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, YES);
  73. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  74. IMP currentIMP = selectorInstrumentor.currentIMP;
  75. [selectorInstrumentor setReplacingBlock:^(id session) {
  76. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  77. if (!strongInstrument) {
  78. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentedClass);
  79. }
  80. typedef NSURLSession *(*OriginalImp)(id, SEL);
  81. NSURLSession *sharedSession = ((OriginalImp)currentIMP)(session, selector);
  82. if ([sharedSession isProxy]) {
  83. [strongInstrument registerProxyObject:sharedSession];
  84. } else {
  85. [strongInstrument registerInstrumentorForClass:[sharedSession class]];
  86. }
  87. return sharedSession;
  88. }];
  89. }
  90. /** Wraps +sessionWithConfiguration:.
  91. *
  92. * @param instrument The FPRNSURLSessionInstrument instance.
  93. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  94. */
  95. FOUNDATION_STATIC_INLINE
  96. void InstrumentSessionWithConfiguration(FPRNSURLSessionInstrument *instrument,
  97. FPRClassInstrumentor *instrumentor) {
  98. SEL selector = @selector(sessionWithConfiguration:);
  99. Class instrumentedClass = instrumentor.instrumentedClass;
  100. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, YES);
  101. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  102. IMP currentIMP = selectorInstrumentor.currentIMP;
  103. [selectorInstrumentor setReplacingBlock:^(id session, NSURLSessionConfiguration *configuration) {
  104. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  105. if (!strongInstrument) {
  106. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentedClass);
  107. }
  108. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *);
  109. NSURLSession *sessionInstance = ((OriginalImp)currentIMP)(session, selector, configuration);
  110. if ([sessionInstance isProxy]) {
  111. [strongInstrument registerProxyObject:sessionInstance];
  112. } else {
  113. [strongInstrument registerInstrumentorForClass:[sessionInstance class]];
  114. }
  115. return sessionInstance;
  116. }];
  117. }
  118. /** Wraps +sessionWithConfiguration:delegate:delegateQueue:.
  119. *
  120. * @param instrument The FPRNSURLSessionInstrument instance.
  121. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  122. * @param delegateInstrument The FPRNSURLSessionDelegateInstrument that will track the delegate
  123. * selectors.
  124. */
  125. FOUNDATION_STATIC_INLINE
  126. void InstrumentSessionWithConfigurationDelegateDelegateQueue(
  127. FPRNSURLSessionInstrument *instrument,
  128. FPRClassInstrumentor *instrumentor,
  129. FPRNSURLSessionDelegateInstrument *delegateInstrument) {
  130. SEL selector = @selector(sessionWithConfiguration:delegate:delegateQueue:);
  131. Class instrumentedClass = instrumentor.instrumentedClass;
  132. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, YES);
  133. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  134. IMP currentIMP = selectorInstrumentor.currentIMP;
  135. [selectorInstrumentor
  136. setReplacingBlock:^(id session, NSURLSessionConfiguration *configuration,
  137. id<NSURLSessionDelegate> delegate, NSOperationQueue *queue) {
  138. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  139. if (!strongInstrument) {
  140. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentedClass);
  141. }
  142. if (delegate) {
  143. [delegateInstrument registerClass:[delegate class]];
  144. [delegateInstrument registerObject:delegate];
  145. } else {
  146. delegate = [[FPRNSURLSessionDelegate alloc] init];
  147. }
  148. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *,
  149. id<NSURLSessionDelegate>, NSOperationQueue *);
  150. NSURLSession *sessionInstance =
  151. ((OriginalImp)currentIMP)([session class], selector, configuration, delegate, queue);
  152. if ([sessionInstance isProxy]) {
  153. [strongInstrument registerProxyObject:sessionInstance];
  154. } else {
  155. [strongInstrument registerInstrumentorForClass:[sessionInstance class]];
  156. }
  157. return sessionInstance;
  158. }];
  159. }
  160. /** Wraps -dataTaskWithURL:.
  161. *
  162. * @param instrument The FPRNSURLSessionInstrument instance.
  163. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  164. */
  165. FOUNDATION_STATIC_INLINE
  166. void InstrumentDataTaskWithURL(FPRNSURLSessionInstrument *instrument,
  167. FPRClassInstrumentor *instrumentor) {
  168. SEL selector = @selector(dataTaskWithURL:);
  169. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  170. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  171. IMP currentIMP = selectorInstrumentor.currentIMP;
  172. [selectorInstrumentor setReplacingBlock:^(id session, NSURL *url) {
  173. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  174. if (!strongInstrument) {
  175. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  176. }
  177. typedef NSURLSessionDataTask *(*OriginalImp)(id, SEL, NSURL *);
  178. NSURLSessionDataTask *dataTask = ((OriginalImp)currentIMP)(session, selector, url);
  179. if (dataTask.originalRequest) {
  180. FPRNetworkTrace *trace =
  181. [[FPRNetworkTrace alloc] initWithURLRequest:dataTask.originalRequest];
  182. [trace start];
  183. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  184. [FPRNetworkTrace addNetworkTrace:trace toObject:dataTask];
  185. }
  186. return dataTask;
  187. }];
  188. }
  189. /** Instruments -dataTaskWithURL:completionHandler:.
  190. *
  191. * @param instrument The FPRNSURLSessionInstrument instance.
  192. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  193. */
  194. FOUNDATION_STATIC_INLINE
  195. void InstrumentDataTaskWithURLCompletionHandler(FPRNSURLSessionInstrument *instrument,
  196. FPRClassInstrumentor *instrumentor) {
  197. SEL selector = @selector(dataTaskWithURL:completionHandler:);
  198. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  199. IMP currentIMP = selectorInstrumentor.currentIMP;
  200. [selectorInstrumentor setReplacingBlock:^(id session, NSURL *URL,
  201. FPRDataTaskCompletionHandler completionHandler) {
  202. __block NSURLSessionDataTask *task = nil;
  203. FPRDataTaskCompletionHandler wrappedCompletionHandler = nil;
  204. if (completionHandler) {
  205. wrappedCompletionHandler = ^(NSData *data, NSURLResponse *response, NSError *error) {
  206. FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:task];
  207. [trace didReceiveData:data];
  208. [trace didCompleteRequestWithResponse:response error:error];
  209. [FPRNetworkTrace removeNetworkTraceFromObject:task];
  210. completionHandler(data, response, error);
  211. };
  212. }
  213. typedef NSURLSessionDataTask *(*OriginalImp)(id, SEL, NSURL *, FPRDataTaskCompletionHandler);
  214. task = ((OriginalImp)currentIMP)(session, selector, URL, wrappedCompletionHandler);
  215. // Add the network trace object only when the trace object is not added to the task object.
  216. if ([FPRNetworkTrace networkTraceFromObject:task] == nil) {
  217. FPRNetworkTrace *trace = [[FPRNetworkTrace alloc] initWithURLRequest:task.originalRequest];
  218. [trace start];
  219. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  220. [FPRNetworkTrace addNetworkTrace:trace toObject:task];
  221. }
  222. return task;
  223. }];
  224. }
  225. /** Wraps -dataTaskWithRequest:.
  226. *
  227. * @param instrument The FPRNSURLSessionInstrument instance.
  228. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  229. */
  230. FOUNDATION_STATIC_INLINE
  231. void InstrumentDataTaskWithRequest(FPRNSURLSessionInstrument *instrument,
  232. FPRClassInstrumentor *instrumentor) {
  233. SEL selector = @selector(dataTaskWithRequest:);
  234. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  235. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  236. IMP currentIMP = selectorInstrumentor.currentIMP;
  237. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request) {
  238. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  239. if (!strongInstrument) {
  240. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  241. }
  242. typedef NSURLSessionDataTask *(*OriginalImp)(id, SEL, NSURLRequest *);
  243. NSURLSessionDataTask *dataTask = ((OriginalImp)currentIMP)(session, selector, request);
  244. if (dataTask.originalRequest) {
  245. FPRNetworkTrace *trace =
  246. [[FPRNetworkTrace alloc] initWithURLRequest:dataTask.originalRequest];
  247. [trace start];
  248. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  249. [FPRNetworkTrace addNetworkTrace:trace toObject:dataTask];
  250. }
  251. return dataTask;
  252. }];
  253. }
  254. /** Instruments -dataTaskWithRequest:completionHandler:.
  255. *
  256. * @param instrument The FPRNSURLSessionInstrument instance.
  257. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  258. */
  259. FOUNDATION_STATIC_INLINE
  260. void InstrumentDataTaskWithRequestCompletionHandler(FPRNSURLSessionInstrument *instrument,
  261. FPRClassInstrumentor *instrumentor) {
  262. SEL selector = @selector(dataTaskWithRequest:completionHandler:);
  263. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  264. IMP currentIMP = selectorInstrumentor.currentIMP;
  265. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request,
  266. FPRDataTaskCompletionHandler completionHandler) {
  267. __block NSURLSessionDataTask *task = nil;
  268. FPRDataTaskCompletionHandler wrappedCompletionHandler = nil;
  269. if (completionHandler) {
  270. wrappedCompletionHandler = ^(NSData *data, NSURLResponse *response, NSError *error) {
  271. FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:task];
  272. [trace didReceiveData:data];
  273. [trace didCompleteRequestWithResponse:response error:error];
  274. [FPRNetworkTrace removeNetworkTraceFromObject:task];
  275. completionHandler(data, response, error);
  276. };
  277. }
  278. typedef NSURLSessionDataTask *(*OriginalImp)(id, SEL, NSURLRequest *,
  279. FPRDataTaskCompletionHandler);
  280. task = ((OriginalImp)currentIMP)(session, selector, request, wrappedCompletionHandler);
  281. // Add the network trace object only when the trace object is not added to the task object.
  282. if ([FPRNetworkTrace networkTraceFromObject:task] == nil) {
  283. FPRNetworkTrace *trace = [[FPRNetworkTrace alloc] initWithURLRequest:task.originalRequest];
  284. [trace start];
  285. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  286. [FPRNetworkTrace addNetworkTrace:trace toObject:task];
  287. }
  288. return task;
  289. }];
  290. }
  291. /** Instruments -uploadTaskWithRequest:fromFile:.
  292. *
  293. * @param instrument The FPRNSURLSessionInstrument instance.
  294. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  295. */
  296. FOUNDATION_STATIC_INLINE
  297. void InstrumentUploadTaskWithRequestFromFile(FPRNSURLSessionInstrument *instrument,
  298. FPRClassInstrumentor *instrumentor) {
  299. SEL selector = @selector(uploadTaskWithRequest:fromFile:);
  300. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  301. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  302. IMP currentIMP = selectorInstrumentor.currentIMP;
  303. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request, NSURL *fileURL) {
  304. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  305. if (!strongInstrument) {
  306. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  307. }
  308. typedef NSURLSessionUploadTask *(*OriginalImp)(id, SEL, NSURLRequest *, NSURL *);
  309. NSURLSessionUploadTask *uploadTask =
  310. ((OriginalImp)currentIMP)(session, selector, request, fileURL);
  311. if (uploadTask.originalRequest) {
  312. FPRNetworkTrace *trace =
  313. [[FPRNetworkTrace alloc] initWithURLRequest:uploadTask.originalRequest];
  314. [trace start];
  315. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  316. [FPRNetworkTrace addNetworkTrace:trace toObject:uploadTask];
  317. }
  318. return uploadTask;
  319. }];
  320. }
  321. /** Instruments -uploadTaskWithRequest:fromFile:completionHandler:.
  322. *
  323. * @param instrument The FPRNSURLSessionInstrument instance.
  324. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  325. */
  326. FOUNDATION_STATIC_INLINE
  327. void InstrumentUploadTaskWithRequestFromFileCompletionHandler(FPRNSURLSessionInstrument *instrument,
  328. FPRClassInstrumentor *instrumentor) {
  329. SEL selector = @selector(uploadTaskWithRequest:fromFile:completionHandler:);
  330. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  331. IMP currentIMP = selectorInstrumentor.currentIMP;
  332. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request, NSURL *fileURL,
  333. FPRDataTaskCompletionHandler completionHandler) {
  334. FPRNetworkTrace *trace = [[FPRNetworkTrace alloc] initWithURLRequest:request];
  335. [trace start];
  336. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  337. [trace didUploadFileWithURL:fileURL];
  338. FPRDataTaskCompletionHandler wrappedCompletionHandler = nil;
  339. if (completionHandler) {
  340. wrappedCompletionHandler = ^(NSData *data, NSURLResponse *response, NSError *error) {
  341. [trace didReceiveData:data];
  342. [trace didCompleteRequestWithResponse:response error:error];
  343. completionHandler(data, response, error);
  344. };
  345. }
  346. typedef NSURLSessionUploadTask *(*OriginalImp)(id, SEL, NSURLRequest *, NSURL *,
  347. FPRDataTaskCompletionHandler);
  348. return ((OriginalImp)currentIMP)(session, selector, request, fileURL, wrappedCompletionHandler);
  349. }];
  350. }
  351. /** Instruments -uploadTaskWithRequest:fromData:.
  352. *
  353. * @param instrument The FPRNSURLSessionInstrument instance.
  354. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  355. */
  356. FOUNDATION_STATIC_INLINE
  357. void InstrumentUploadTaskWithRequestFromData(FPRNSURLSessionInstrument *instrument,
  358. FPRClassInstrumentor *instrumentor) {
  359. SEL selector = @selector(uploadTaskWithRequest:fromData:);
  360. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  361. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  362. IMP currentIMP = selectorInstrumentor.currentIMP;
  363. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request, NSData *bodyData) {
  364. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  365. if (!strongInstrument) {
  366. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  367. }
  368. typedef NSURLSessionUploadTask *(*OriginalImp)(id, SEL, NSURLRequest *, NSData *);
  369. NSURLSessionUploadTask *uploadTask =
  370. ((OriginalImp)currentIMP)(session, selector, request, bodyData);
  371. if (uploadTask.originalRequest) {
  372. FPRNetworkTrace *trace =
  373. [[FPRNetworkTrace alloc] initWithURLRequest:uploadTask.originalRequest];
  374. [trace start];
  375. trace.requestSize = bodyData.length;
  376. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  377. [FPRNetworkTrace addNetworkTrace:trace toObject:uploadTask];
  378. }
  379. return uploadTask;
  380. }];
  381. }
  382. /** Instruments -uploadTaskWithRequest:fromData:completionHandler:.
  383. *
  384. * @param instrument The FPRNSURLSessionInstrument instance.
  385. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  386. */
  387. FOUNDATION_STATIC_INLINE
  388. void InstrumentUploadTaskWithRequestFromDataCompletionHandler(FPRNSURLSessionInstrument *instrument,
  389. FPRClassInstrumentor *instrumentor) {
  390. SEL selector = @selector(uploadTaskWithRequest:fromData:completionHandler:);
  391. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  392. IMP currentIMP = selectorInstrumentor.currentIMP;
  393. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request, NSData *bodyData,
  394. FPRDataTaskCompletionHandler completionHandler) {
  395. FPRNetworkTrace *trace = [[FPRNetworkTrace alloc] initWithURLRequest:request];
  396. [trace start];
  397. trace.requestSize = bodyData.length;
  398. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  399. FPRDataTaskCompletionHandler wrappedCompletionHandler = nil;
  400. if (completionHandler) {
  401. wrappedCompletionHandler = ^(NSData *data, NSURLResponse *response, NSError *error) {
  402. [trace didReceiveData:data];
  403. [trace didCompleteRequestWithResponse:response error:error];
  404. completionHandler(data, response, error);
  405. };
  406. }
  407. typedef NSURLSessionUploadTask *(*OriginalImp)(id, SEL, NSURLRequest *, NSData *,
  408. FPRDataTaskCompletionHandler);
  409. return ((OriginalImp)currentIMP)(session, selector, request, bodyData,
  410. wrappedCompletionHandler);
  411. }];
  412. }
  413. /** Instruments -uploadTaskWithStreamedRequest:.
  414. *
  415. * @param instrument The FPRNSURLSessionInstrument instance.
  416. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  417. */
  418. FOUNDATION_STATIC_INLINE
  419. void InstrumentUploadTaskWithStreamedRequest(FPRNSURLSessionInstrument *instrument,
  420. FPRClassInstrumentor *instrumentor) {
  421. SEL selector = @selector(uploadTaskWithStreamedRequest:);
  422. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  423. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  424. IMP currentIMP = selectorInstrumentor.currentIMP;
  425. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request) {
  426. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  427. if (!strongInstrument) {
  428. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  429. }
  430. typedef NSURLSessionUploadTask *(*OriginalImp)(id, SEL, NSURLRequest *);
  431. NSURLSessionUploadTask *uploadTask = ((OriginalImp)currentIMP)(session, selector, request);
  432. if (uploadTask.originalRequest) {
  433. FPRNetworkTrace *trace =
  434. [[FPRNetworkTrace alloc] initWithURLRequest:uploadTask.originalRequest];
  435. [trace start];
  436. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  437. [FPRNetworkTrace addNetworkTrace:trace toObject:uploadTask];
  438. }
  439. return uploadTask;
  440. }];
  441. }
  442. /** Instruments -downloadTaskWithURL:.
  443. *
  444. * @param instrument The FPRNSURLSessionInstrument instance.
  445. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  446. */
  447. FOUNDATION_STATIC_INLINE
  448. void InstrumentDownloadTaskWithURL(FPRNSURLSessionInstrument *instrument,
  449. FPRClassInstrumentor *instrumentor) {
  450. SEL selector = @selector(downloadTaskWithURL:);
  451. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  452. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  453. IMP currentIMP = selectorInstrumentor.currentIMP;
  454. [selectorInstrumentor setReplacingBlock:^(id session, NSURL *url) {
  455. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  456. if (!strongInstrument) {
  457. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  458. }
  459. typedef NSURLSessionDownloadTask *(*OriginalImp)(id, SEL, NSURL *);
  460. NSURLSessionDownloadTask *downloadTask = ((OriginalImp)currentIMP)(session, selector, url);
  461. if (downloadTask.originalRequest) {
  462. FPRNetworkTrace *trace =
  463. [[FPRNetworkTrace alloc] initWithURLRequest:downloadTask.originalRequest];
  464. [trace start];
  465. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  466. [FPRNetworkTrace addNetworkTrace:trace toObject:downloadTask];
  467. }
  468. return downloadTask;
  469. }];
  470. }
  471. /** Instruments -downloadTaskWithURL:completionHandler:.
  472. *
  473. * @param instrument The FPRNSURLSessionInstrument instance.
  474. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  475. */
  476. FOUNDATION_STATIC_INLINE
  477. void InstrumentDownloadTaskWithURLCompletionHandler(FPRNSURLSessionInstrument *instrument,
  478. FPRClassInstrumentor *instrumentor) {
  479. SEL selector = @selector(downloadTaskWithURL:completionHandler:);
  480. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  481. IMP currentIMP = selectorInstrumentor.currentIMP;
  482. [selectorInstrumentor setReplacingBlock:^(id session, NSURL *URL,
  483. FPRDownloadTaskCompletionHandler completionHandler) {
  484. __block NSURLSessionDownloadTask *downloadTask = nil;
  485. FPRDownloadTaskCompletionHandler wrappedCompletionHandler = nil;
  486. if (completionHandler) {
  487. wrappedCompletionHandler = ^(NSURL *location, NSURLResponse *response, NSError *error) {
  488. FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:downloadTask];
  489. [trace didReceiveFileURL:location];
  490. [trace didCompleteRequestWithResponse:response error:error];
  491. completionHandler(location, response, error);
  492. };
  493. }
  494. typedef NSURLSessionDownloadTask *(*OriginalImp)(id, SEL, NSURL *,
  495. FPRDownloadTaskCompletionHandler);
  496. downloadTask = ((OriginalImp)currentIMP)(session, selector, URL, wrappedCompletionHandler);
  497. // Add the network trace object only when the trace object is not added to the task object.
  498. if ([FPRNetworkTrace networkTraceFromObject:downloadTask] == nil) {
  499. FPRNetworkTrace *trace =
  500. [[FPRNetworkTrace alloc] initWithURLRequest:downloadTask.originalRequest];
  501. [trace start];
  502. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  503. [FPRNetworkTrace addNetworkTrace:trace toObject:downloadTask];
  504. }
  505. return downloadTask;
  506. }];
  507. }
  508. /** Instruments -downloadTaskWithRequest:.
  509. *
  510. * @param instrument The FPRNSURLSessionInstrument instance.
  511. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  512. */
  513. FOUNDATION_STATIC_INLINE
  514. void InstrumentDownloadTaskWithRequest(FPRNSURLSessionInstrument *instrument,
  515. FPRClassInstrumentor *instrumentor) {
  516. SEL selector = @selector(downloadTaskWithRequest:);
  517. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  518. __weak FPRNSURLSessionInstrument *weakInstrument = instrument;
  519. IMP currentIMP = selectorInstrumentor.currentIMP;
  520. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request) {
  521. __strong FPRNSURLSessionInstrument *strongInstrument = weakInstrument;
  522. if (!strongInstrument) {
  523. ThrowExceptionBecauseInstrumentHasBeenDeallocated(selector, instrumentor.instrumentedClass);
  524. }
  525. typedef NSURLSessionDownloadTask *(*OriginalImp)(id, SEL, NSURLRequest *);
  526. NSURLSessionDownloadTask *downloadTask = ((OriginalImp)currentIMP)(session, selector, request);
  527. if (downloadTask.originalRequest) {
  528. FPRNetworkTrace *trace =
  529. [[FPRNetworkTrace alloc] initWithURLRequest:downloadTask.originalRequest];
  530. [trace start];
  531. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  532. [FPRNetworkTrace addNetworkTrace:trace toObject:downloadTask];
  533. }
  534. return downloadTask;
  535. }];
  536. }
  537. /** Instruments -downloadTaskWithRequest:completionHandler:.
  538. *
  539. * @param instrument The FPRNSURLSessionInstrument instance.
  540. * @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
  541. */
  542. FOUNDATION_STATIC_INLINE
  543. void InstrumentDownloadTaskWithRequestCompletionHandler(FPRNSURLSessionInstrument *instrument,
  544. FPRClassInstrumentor *instrumentor) {
  545. SEL selector = @selector(downloadTaskWithRequest:completionHandler:);
  546. FPRSelectorInstrumentor *selectorInstrumentor = SelectorInstrumentor(selector, instrumentor, NO);
  547. IMP currentIMP = selectorInstrumentor.currentIMP;
  548. [selectorInstrumentor setReplacingBlock:^(id session, NSURLRequest *request,
  549. FPRDownloadTaskCompletionHandler completionHandler) {
  550. __block NSURLSessionDownloadTask *downloadTask = nil;
  551. FPRDownloadTaskCompletionHandler wrappedCompletionHandler = nil;
  552. if (completionHandler) {
  553. wrappedCompletionHandler = ^(NSURL *location, NSURLResponse *response, NSError *error) {
  554. FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:downloadTask];
  555. [trace didReceiveFileURL:location];
  556. [trace didCompleteRequestWithResponse:response error:error];
  557. completionHandler(location, response, error);
  558. };
  559. }
  560. typedef NSURLSessionDownloadTask *(*OriginalImp)(id, SEL, NSURLRequest *,
  561. FPRDownloadTaskCompletionHandler);
  562. downloadTask = ((OriginalImp)currentIMP)(session, selector, request, wrappedCompletionHandler);
  563. // Add the network trace object only when the trace object is not added to the task object.
  564. if ([FPRNetworkTrace networkTraceFromObject:downloadTask] == nil) {
  565. FPRNetworkTrace *trace =
  566. [[FPRNetworkTrace alloc] initWithURLRequest:downloadTask.originalRequest];
  567. [trace start];
  568. [trace checkpointState:FPRNetworkTraceCheckpointStateInitiated];
  569. [FPRNetworkTrace addNetworkTrace:trace toObject:downloadTask];
  570. }
  571. return downloadTask;
  572. }];
  573. }
  574. #pragma mark - FPRNSURLSessionInstrument
  575. @implementation FPRNSURLSessionInstrument
  576. - (instancetype)init {
  577. self = [super init];
  578. if (self) {
  579. _delegateInstrument = [[FPRNSURLSessionDelegateInstrument alloc] init];
  580. [_delegateInstrument registerInstrumentors];
  581. }
  582. return self;
  583. }
  584. - (void)registerInstrumentors {
  585. [self registerInstrumentorForClass:[NSURLSession class]];
  586. }
  587. - (void)deregisterInstrumentors {
  588. [_delegateInstrument deregisterInstrumentors];
  589. [super deregisterInstrumentors];
  590. }
  591. - (void)registerInstrumentorForClass:(Class)aClass {
  592. dispatch_sync(GetInstrumentationQueue(), ^{
  593. FPRAssert([aClass isSubclassOfClass:[NSURLSession class]],
  594. @"Class %@ is not a subclass of "
  595. "NSURLSession",
  596. aClass);
  597. // If this class has already been instrumented, just return.
  598. FPRClassInstrumentor *instrumentor = [[FPRClassInstrumentor alloc] initWithClass:aClass];
  599. if (![self registerClassInstrumentor:instrumentor]) {
  600. return;
  601. }
  602. InstrumentSharedSession(self, instrumentor);
  603. InstrumentSessionWithConfiguration(self, instrumentor);
  604. InstrumentSessionWithConfigurationDelegateDelegateQueue(self, instrumentor,
  605. _delegateInstrument);
  606. InstrumentDataTaskWithURL(self, instrumentor);
  607. InstrumentDataTaskWithURLCompletionHandler(self, instrumentor);
  608. InstrumentDataTaskWithRequest(self, instrumentor);
  609. InstrumentDataTaskWithRequestCompletionHandler(self, instrumentor);
  610. InstrumentUploadTaskWithRequestFromFile(self, instrumentor);
  611. InstrumentUploadTaskWithRequestFromFileCompletionHandler(self, instrumentor);
  612. InstrumentUploadTaskWithRequestFromData(self, instrumentor);
  613. InstrumentUploadTaskWithRequestFromDataCompletionHandler(self, instrumentor);
  614. InstrumentUploadTaskWithStreamedRequest(self, instrumentor);
  615. InstrumentDownloadTaskWithURL(self, instrumentor);
  616. InstrumentDownloadTaskWithURLCompletionHandler(self, instrumentor);
  617. InstrumentDownloadTaskWithRequest(self, instrumentor);
  618. InstrumentDownloadTaskWithRequestCompletionHandler(self, instrumentor);
  619. [instrumentor swizzle];
  620. });
  621. }
  622. - (void)registerProxyObject:(id)proxy {
  623. [FPRProxyObjectHelper registerProxyObject:proxy
  624. forSuperclass:[NSURLSession class]
  625. varFoundHandler:^(id ivar) {
  626. [self registerInstrumentorForClass:[ivar class]];
  627. }];
  628. }
  629. @end