@@ -10,6 +10,14 @@ import OpenTelemetrySdk
1010struct NetworkRequestState {
1111 var request : URLRequest ?
1212 var dataProcessed : Data ?
13+
14+ mutating func setRequest( _ request: URLRequest ) {
15+ self . request = request
16+ }
17+
18+ mutating func setData( _ data: URLRequest ) {
19+ self . request = data
20+ }
1321}
1422
1523private var idKey : Void ?
@@ -68,10 +76,13 @@ public class URLSessionInstrumentation {
6876 }
6977 }
7078 }
71- injectIntoNSURLSessionCreateTaskMethods ( )
79+ if #available( OSX 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * ) {
80+ injectIntoNSURLSessionCreateTaskMethods ( )
81+ }
7282 injectIntoNSURLSessionCreateTaskWithParameterMethods ( )
7383 injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods ( )
7484 injectIntoNSURLSessionAsyncUploadTaskMethods ( )
85+ injectIntoNSURLSessionTaskResume ( )
7586 }
7687
7788 private func injectIntoDelegateClass( cls: AnyClass ) {
@@ -207,23 +218,26 @@ public class URLSessionInstrumentation {
207218 var task : URLSessionTask !
208219
209220 var completionBlock = completion
210- if objc_getAssociatedObject ( argument, & idKey) == nil {
211- let completionWrapper : ( Any ? , URLResponse ? , Error ? ) -> Void = { object, response, error in
212- if error != nil {
213- let status = ( response as? HTTPURLResponse ) ? . statusCode ?? 0
214- URLSessionLogger . logError ( error!, dataOrFile: object, statusCode: status, instrumentation: self , sessionTaskId: sessionTaskId)
215- } else {
216- if let response = response {
217- URLSessionLogger . logResponse ( response, dataOrFile: object, instrumentation: self , sessionTaskId: sessionTaskId)
221+
222+ if completionBlock != nil {
223+ if objc_getAssociatedObject ( argument, & idKey) == nil {
224+ let completionWrapper : ( Any ? , URLResponse ? , Error ? ) -> Void = { object, response, error in
225+ if error != nil {
226+ let status = ( response as? HTTPURLResponse ) ? . statusCode ?? 0
227+ URLSessionLogger . logError ( error!, dataOrFile: object, statusCode: status, instrumentation: self , sessionTaskId: sessionTaskId)
228+ } else {
229+ if let response = response {
230+ URLSessionLogger . logResponse ( response, dataOrFile: object, instrumentation: self , sessionTaskId: sessionTaskId)
231+ }
232+ }
233+ if let completion = completion {
234+ completion ( object, response, error)
235+ } else {
236+ ( session. delegate as? URLSessionTaskDelegate ) ? . urlSession ? ( session, task: task, didCompleteWithError: error)
218237 }
219238 }
220- if let completion = completion {
221- completion ( object, response, error)
222- } else {
223- ( session. delegate as? URLSessionTaskDelegate ) ? . urlSession ? ( session, task: task, didCompleteWithError: error)
224- }
239+ completionBlock = completionWrapper
225240 }
226- completionBlock = completionWrapper
227241 }
228242
229243 if let request = argument as? URLRequest , objc_getAssociatedObject ( argument, & idKey) == nil {
@@ -295,6 +309,45 @@ public class URLSessionInstrumentation {
295309 }
296310 }
297311
312+ private func injectIntoNSURLSessionTaskResume( ) {
313+ var methodsToSwizzle = [ Method] ( )
314+
315+ if let method = class_getInstanceMethod ( URLSessionTask . self, #selector( URLSessionTask . resume) ) {
316+ methodsToSwizzle. append ( method)
317+ }
318+
319+ if let cfURLSession = NSClassFromString ( " __NSCFURLSessionTask " ) ,
320+ let method = class_getInstanceMethod ( cfURLSession, NSSelectorFromString ( " resume " ) )
321+ {
322+ methodsToSwizzle. append ( method)
323+ }
324+
325+ if NSClassFromString ( " AFURLSessionManager " ) != nil {
326+ let classes = InstrumentationUtils . objc_getClassList ( )
327+ classes. forEach {
328+ if let method = class_getInstanceMethod ( $0, NSSelectorFromString ( " af_resume " ) ) {
329+ methodsToSwizzle. append ( method)
330+ }
331+ }
332+ }
333+
334+ methodsToSwizzle. forEach {
335+ let theMethod = $0
336+
337+ var originalIMP : IMP ?
338+ let block : @convention ( block) ( URLSessionTask ) -> Void = { anyTask in
339+ self . urlSessionTaskWillResume ( anyTask)
340+ let key = String ( theMethod. hashValue)
341+ objc_setAssociatedObject ( anyTask, key, true , . OBJC_ASSOCIATION_RETAIN_NONATOMIC)
342+ let castedIMP = unsafeBitCast ( originalIMP, to: ( @convention( c) ( Any) - > Void) . self)
343+ castedIMP ( anyTask)
344+ objc_setAssociatedObject ( anyTask, key, nil , . OBJC_ASSOCIATION_RETAIN_NONATOMIC)
345+ }
346+ let swizzledIMP = imp_implementationWithBlock ( unsafeBitCast ( block, to: AnyObject . self) )
347+ originalIMP = method_setImplementation ( theMethod, swizzledIMP)
348+ }
349+ }
350+
298351 // Delegate methods
299352 private func injectTaskDidReceiveDataIntoDelegateClass( cls: AnyClass ) {
300353 let selector = #selector( URLSessionDataDelegate . urlSession ( _: dataTask: didReceive: ) )
@@ -429,8 +482,11 @@ public class URLSessionInstrumentation {
429482 queue. async {
430483 let taskId = self . idKeyForTask ( dataTask)
431484 if ( self . requestMap [ taskId] ? . request) != nil {
432- var requestState = self . requestState ( for: taskId)
433- requestState. dataProcessed? . append ( dataCopy)
485+ self . createRequestState ( for: taskId)
486+ if self . requestMap [ taskId] ? . dataProcessed == nil {
487+ self . requestMap [ taskId] ? . dataProcessed = Data ( )
488+ }
489+ self . requestMap [ taskId] ? . dataProcessed? . append ( dataCopy)
434490 }
435491 }
436492 }
@@ -440,11 +496,11 @@ public class URLSessionInstrumentation {
440496 queue. async {
441497 let taskId = self . idKeyForTask ( dataTask)
442498 if ( self . requestMap [ taskId] ? . request) != nil {
443- var requestState = self . requestState ( for: taskId)
499+ self . createRequestState ( for: taskId)
444500 if response. expectedContentLength < 0 {
445- requestState . dataProcessed = Data ( )
501+ self . requestMap [ taskId ] ? . dataProcessed = Data ( )
446502 } else {
447- requestState . dataProcessed = Data ( capacity: Int ( response. expectedContentLength) )
503+ self . requestMap [ taskId ] ? . dataProcessed = Data ( capacity: Int ( response. expectedContentLength) )
448504 }
449505 }
450506 }
@@ -453,11 +509,8 @@ public class URLSessionInstrumentation {
453509 private func urlSession( _ session: URLSession , task: URLSessionTask , didCompleteWithError error: Error ? ) {
454510 let taskId = self . idKeyForTask ( task)
455511
456- var requestState : NetworkRequestState ?
457- if ( self . requestMap [ taskId] ? . request) != nil {
458- requestState = self . requestState ( for: taskId)
459- }
460-
512+ let requestState = self . requestMap [ taskId]
513+
461514 if let error = error {
462515 let status = ( task. response as? HTTPURLResponse ) ? . statusCode ?? 0
463516 URLSessionLogger . logError ( error, dataOrFile: requestState? . dataProcessed, statusCode: status, instrumentation: self , sessionTaskId: taskId)
@@ -483,6 +536,18 @@ public class URLSessionInstrumentation {
483536 }
484537 }
485538
539+ private func urlSessionTaskWillResume( _ session: URLSessionTask ) {
540+ let taskId = self . idKeyForTask ( session)
541+ if let request = session. currentRequest {
542+ var state = requestMap [ taskId]
543+ if state == nil {
544+ state = NetworkRequestState ( )
545+ requestMap [ taskId] = state
546+ }
547+ requestMap [ taskId] ? . setRequest ( request)
548+ }
549+ }
550+
486551 // Helpers
487552 private func idKeyForTask( _ task: URLSessionTask ) -> String {
488553 var id = objc_getAssociatedObject ( task, & idKey) as? String
@@ -497,12 +562,11 @@ public class URLSessionInstrumentation {
497562 objc_setAssociatedObject ( task, & idKey, value, . OBJC_ASSOCIATION_RETAIN_NONATOMIC)
498563 }
499564
500- private func requestState ( for id: String ) -> NetworkRequestState {
565+ private func createRequestState ( for id: String ) {
501566 var state = requestMap [ id]
502- if state == nil {
567+ if requestMap [ id ] == nil {
503568 state = NetworkRequestState ( )
504569 requestMap [ id] = state
505570 }
506- return state!
507571 }
508572}
0 commit comments