added files via upload

This commit is contained in:
Balackburn
2023-06-27 09:54:41 +02:00
commit 2ff6aac218
1420 changed files with 88898 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
//
// FLEXNetworkObserver.h
// Derived from:
//
// PDAFNetworkDomainController.h
// PonyDebugger
//
// Created by Mike Lewis on 2/27/12.
//
// Licensed to Square, Inc. under one or more contributor license agreements.
// See the LICENSE file distributed with this work for the terms under
// which Square, Inc. licenses this file to you.
//
#import <Foundation/Foundation.h>
FOUNDATION_EXTERN NSString *const kFLEXNetworkObserverEnabledStateChangedNotification;
/// This class swizzles NSURLConnection and NSURLSession delegate methods to observe events in the URL loading system.
/// High level network events are sent to the default FLEXNetworkRecorder instance which maintains the request history and caches response bodies.
@interface FLEXNetworkObserver : NSObject
/// Swizzling occurs when the observer is enabled for the first time.
/// This reduces the impact of FLEX if network debugging is not desired.
/// NOTE: this setting persists between launches of the app.
@property (nonatomic, class, getter=isEnabled) BOOL enabled;
@end

View File

@@ -0,0 +1,1997 @@
//
// FLEXNetworkObserver.m
// Derived from:
//
// PDAFNetworkDomainController.m
// PonyDebugger
//
// Created by Mike Lewis on 2/27/12.
//
// Licensed to Square, Inc. under one or more contributor license agreements.
// See the LICENSE file distributed with this work for the terms under
// which Square, Inc. licenses this file to you.
//
// Heavily modified and added to by Tanner Bennett and various other contributors.
// git blame details these modifications.
//
#import "FLEXNetworkObserver.h"
#import "FLEXNetworkRecorder.h"
#import "FLEXUtility.h"
#import "NSUserDefaults+FLEX.h"
#import "NSObject+FLEX_Reflection.h"
#import "FLEXMethod.h"
#import "Firestore.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <dispatch/queue.h>
#include <dlfcn.h>
NSString *const kFLEXNetworkObserverEnabledStateChangedNotification = @"kFLEXNetworkObserverEnabledStateChangedNotification";
typedef void (^NSURLSessionAsyncCompletion)(id fileURLOrData, NSURLResponse *response, NSError *error);
typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)(NSURLSession *, id, NSURLSessionAsyncCompletion);
@interface FLEXInternalRequestState : NSObject
@property (nonatomic, copy) NSURLRequest *request;
@property (nonatomic) NSMutableData *dataAccumulator;
@end
@implementation FLEXInternalRequestState
@end
@interface FLEXNetworkObserver (NSURLConnectionHelpers)
- (void)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response delegate:(id<NSURLConnectionDelegate>)delegate;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response delegate:(id<NSURLConnectionDelegate>)delegate;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data delegate:(id<NSURLConnectionDelegate>)delegate;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection delegate:(id<NSURLConnectionDelegate>)delegate;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error delegate:(id<NSURLConnectionDelegate>)delegate;
- (void)connectionWillCancel:(NSURLConnection *)connection;
@end
@interface FLEXNetworkObserver (NSURLSessionTaskHelpers)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data delegate:(id<NSURLSessionDelegate>)delegate;
- (void)URLSessionTaskWillResume:(NSURLSessionTask *)task;
- (void)websocketTask:(NSURLSessionWebSocketTask *)task
sendMessagage:(NSURLSessionWebSocketMessage *)message API_AVAILABLE(ios(13.0));
- (void)websocketTaskMessageSendCompletion:(NSURLSessionWebSocketMessage *)message
error:(NSError *)error API_AVAILABLE(ios(13.0));
- (void)websocketTask:(NSURLSessionWebSocketTask *)task
receiveMessagage:(NSURLSessionWebSocketMessage *)message
error:(NSError *)error API_AVAILABLE(ios(13.0));
@end
@interface FLEXNetworkObserver ()
@property (nonatomic) NSMutableDictionary<NSString *, FLEXInternalRequestState *> *requestStatesForRequestIDs;
@property (nonatomic) dispatch_queue_t queue;
@end
@implementation FLEXNetworkObserver
#pragma mark - Public Methods
+ (void)setEnabled:(BOOL)enabled {
BOOL previouslyEnabled = [self isEnabled];
NSUserDefaults.standardUserDefaults.flex_networkObserverEnabled = enabled;
if (enabled) {
// Inject if needed. This injection is protected with a dispatch_once, so we're ok calling it multiple times.
// By doing the injection lazily, we keep the impact of the tool lower when this feature isn't enabled.
[self setNetworkMonitorHooks];
}
if (previouslyEnabled != enabled) {
[NSNotificationCenter.defaultCenter postNotificationName:kFLEXNetworkObserverEnabledStateChangedNotification object:self];
}
}
+ (BOOL)isEnabled {
return NSUserDefaults.standardUserDefaults.flex_networkObserverEnabled;
}
+ (void)load {
// We don't want to do the swizzling from +load because not all the
// delegate classes we want to hook may be loaded at this point.
// However, Firebase classes will definitely be loaded by now,
// so we can definitely hook those sooner if need be.
dispatch_async(dispatch_get_main_queue(), ^{
if ([self isEnabled]) {
[self setNetworkMonitorHooks];
}
});
}
#pragma mark - Statics
+ (instancetype)sharedObserver {
static FLEXNetworkObserver *sharedObserver = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObserver = [self new];
});
return sharedObserver;
}
+ (NSString *)nextRequestID {
return NSUUID.UUID.UUIDString;
}
#pragma mark Delegate Injection Convenience Methods
/// All swizzled delegate methods should make use of this guard.
/// This will prevent duplicated sniffing when the original implementation calls up to a superclass
/// implementation which we've also swizzled. The superclass implementation (and implementations in
/// classes above that) will be executed without interference if called from the original implementation.
+ (void)sniffWithoutDuplicationForObject:(NSObject *)object selector:(SEL)selector
sniffingBlock:(void (^)(void))sniffingBlock originalImplementationBlock:(void (^)(void))originalImplementationBlock {
// If we don't have an object to detect nested calls on, just run the original implementation and bail.
// This case can happen if someone besides the URL loading system calls the delegate methods directly.
// See https://github.com/Flipboard/FLEX/issues/61 for an example.
if (!object) {
originalImplementationBlock();
return;
}
const void *key = selector;
// Don't run the sniffing block if we're inside a nested call
if (!objc_getAssociatedObject(object, key)) {
sniffingBlock();
}
// Mark that we're calling through to the original so we can detect nested calls
objc_setAssociatedObject(object, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
originalImplementationBlock();
objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - Hooking
static void (*_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock);
static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock);
static void (*_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$)(
_LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock);
static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$(
_LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock);
static void (*_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, BOOL, void (^)(NSError *));
static void (*_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, NSArray *, void (^)(NSError *));
static void (*_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, void (^)(NSError *));
static void (*_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, void (^)(NSError *));
static void _logos_register_hook(Class _class, SEL _cmd, IMP _new, IMP *_old) {
unsigned int _count, _i;
Class _searchedClass = _class;
Method *_methods;
while (_searchedClass) {
_methods = class_copyMethodList(_searchedClass, &_count);
for (_i = 0; _i < _count; _i++) {
if (method_getName(_methods[_i]) == _cmd) {
if (_class == _searchedClass) {
*_old = method_getImplementation(_methods[_i]);
*_old = class_replaceMethod(_class, _cmd, _new, method_getTypeEncoding(_methods[_i]));
} else {
class_addMethod(_class, _cmd, _new, method_getTypeEncoding(_methods[_i]));
}
free(_methods);
return;
}
}
free(_methods);
_searchedClass = class_getSuperclass(_searchedClass);
}
}
static Class _logos_superclass$_ungrouped$FIRDocumentReference;
static void (*_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$)(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock);
static Class _logos_superclass$_ungrouped$FIRQuery;
static void (*_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$)(
_LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock);
static Class _logos_superclass$_ungrouped$FIRCollectionReference;
static FIRDocumentReference * (*_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$)(
_LOGOS_SELF_TYPE_NORMAL FIRCollectionReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, void (^)(NSError *error));
#pragma mark Firebase, Reading Data
static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST self, SEL _cmd, FIRDocumentSnapshotBlock completion) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder recordFIRDocumentWillFetch:self withTransactionID:requestID];
// Hook callback
FIRDocumentSnapshotBlock orig = completion;
completion = ^(FIRDocumentSnapshot *document, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDocumentDidFetch:document error:error transactionID:requestID];
orig(document, error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$ ? _logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(getDocumentWithCompletion:)))(self, _cmd, completion);
}
static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$(
_LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST self, SEL _cmd, FIRQuerySnapshotBlock completion) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder recordFIRQueryWillFetch:self withTransactionID:requestID];
// Hook callback
FIRQuerySnapshotBlock orig = completion;
completion = ^(FIRQuerySnapshot *query, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRQueryDidFetch:query error:error transactionID:requestID];
orig(query, error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$ ? _logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRQuery, @selector(getDocumentsWithCompletion:)))(self, _cmd, completion);
}
#pragma mark Firebase, Writing Data
static void _logos_method$_ungrouped$FIRDocumentReference$setData$merge$completion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self,
SEL __unused _cmd, NSDictionary<NSString *, id> * documentData, BOOL merge, void (^completion)(NSError *)) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder
recordFIRWillSetData:self
data:documentData
merge:@(merge)
mergeFields:nil
transactionID:requestID
];
// Hook callback
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
orig(error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(setData:merge:completion:)))(self, _cmd, documentData, merge, completion);
}
static void _logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self,
SEL __unused _cmd, NSDictionary<NSString *, id> * documentData,
NSArray * mergeFields, void (^completion)(NSError *)) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder
recordFIRWillSetData:self
data:documentData
merge:nil
mergeFields:mergeFields
transactionID:requestID
];
// Hook callback
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
orig(error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(setData:mergeFields:completion:)))(self, _cmd, documentData, mergeFields, completion);
}
static void _logos_method$_ungrouped$FIRDocumentReference$updateData$completion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self,
SEL __unused _cmd, NSDictionary<id, id> * fields, void (^completion)(NSError *)) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder recordFIRWillUpdateData:self fields:fields transactionID:requestID];
// Hook callback
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidUpdateData:error transactionID:requestID];
orig(error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(updateData:completion:)))(self, _cmd, fields, completion);
}
static void _logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$(
_LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self,
SEL __unused _cmd, void (^completion)(NSError *)) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder recordFIRWillDeleteDocument:self transactionID:requestID];
// Hook callback
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidDeleteDocument:error transactionID:requestID];
orig(error);
};
// Forward invocation
(_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$ ? _logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(deleteDocumentWithCompletion:)))(self, _cmd, completion);
}
static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$(
_LOGOS_SELF_TYPE_NORMAL FIRCollectionReference * _LOGOS_SELF_CONST __unused self,
SEL __unused _cmd, NSDictionary<NSString *, id> * data, void (^completion)(NSError *error)) {
// Generate transaction ID
NSString *requestID = [FLEXNetworkObserver nextRequestID];
// Hook callback
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidAddDocument:error transactionID:requestID];
orig(error);
};
// Forward invocation
FIRDocumentReference *ret = (_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$ ? _logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRCollectionReference, @selector(addDocumentWithData:completion:)))(self, _cmd, data, completion);
// Record transaction start
[FLEXNetworkRecorder.defaultRecorder recordFIRWillAddDocument:self document:ret transactionID:requestID];
// Return
return ret;
}
+ (void)setNetworkMonitorHooks {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self hookFirebaseThings];
[self injectIntoAllNSURLThings];
});
}
+ (void)hookFirebaseThings {
Class _logos_class$_ungrouped$FIRDocumentReference = objc_getClass("FIRDocumentReference");
_logos_superclass$_ungrouped$FIRDocumentReference = class_getSuperclass(_logos_class$_ungrouped$FIRDocumentReference);
Class _logos_class$_ungrouped$FIRQuery = objc_getClass("FIRQuery");
_logos_superclass$_ungrouped$FIRQuery = class_getSuperclass(_logos_class$_ungrouped$FIRQuery);
Class _logos_class$_ungrouped$FIRCollectionReference = objc_getClass("FIRCollectionReference");
_logos_superclass$_ungrouped$FIRCollectionReference = class_getSuperclass(_logos_class$_ungrouped$FIRCollectionReference);
// Reading //
_logos_register_hook(
_logos_class$_ungrouped$FIRDocumentReference,
@selector(getDocumentWithCompletion:),
(IMP)&_logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$,
(IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$
);
_logos_register_hook(
_logos_class$_ungrouped$FIRQuery,
@selector(getDocumentsWithCompletion:),
(IMP)&_logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$,
(IMP *)&_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$
);
// Writing //
_logos_register_hook(
_logos_class$_ungrouped$FIRDocumentReference,
@selector(setData:merge:completion:),
(IMP)&_logos_method$_ungrouped$FIRDocumentReference$setData$merge$completion$,
(IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$
);
_logos_register_hook(
_logos_class$_ungrouped$FIRDocumentReference,
@selector(setData:mergeFields:completion:),
(IMP)&_logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$,
(IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$
);
_logos_register_hook(
_logos_class$_ungrouped$FIRDocumentReference,
@selector(updateData:completion:),
(IMP)&_logos_method$_ungrouped$FIRDocumentReference$updateData$completion$,
(IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$
);
_logos_register_hook(
_logos_class$_ungrouped$FIRDocumentReference,
@selector(deleteDocumentWithCompletion:),
(IMP)&_logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$,
(IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$
);
_logos_register_hook(
_logos_class$_ungrouped$FIRCollectionReference,
@selector(addDocumentWithData:completion:),
(IMP)&_logos_method$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$,
(IMP *)&_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$
);
}
+ (void)injectIntoAllNSURLThings {
// Only allow swizzling once.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Swizzle any classes that implement one of these selectors.
const SEL selectors[] = {
@selector(connectionDidFinishLoading:),
@selector(connection:willSendRequest:redirectResponse:),
@selector(connection:didReceiveResponse:),
@selector(connection:didReceiveData:),
@selector(connection:didFailWithError:),
@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:),
@selector(URLSession:dataTask:didReceiveData:),
@selector(URLSession:dataTask:didReceiveResponse:completionHandler:),
@selector(URLSession:task:didCompleteWithError:),
@selector(URLSession:dataTask:didBecomeDownloadTask:),
@selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:),
@selector(URLSession:downloadTask:didFinishDownloadingToURL:)
};
const int numSelectors = sizeof(selectors) / sizeof(SEL);
Class *classes = NULL;
int numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (NSInteger classIndex = 0; classIndex < numClasses; ++classIndex) {
Class class = classes[classIndex];
if (class == [FLEXNetworkObserver class]) {
continue;
}
// Use the C API rather than NSObject methods to avoid sending messages
// to classes we're not interested in swizzling, which could result
// in us calling +initialize on potentially uninitialized classes.
// NOTE: calling class_getInstanceMethod() DOES send +initialize
// to the class. That's why we iterate through the method list.
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(class, &methodCount);
BOOL matchingSelectorFound = NO;
for (unsigned int methodIndex = 0; methodIndex < methodCount; methodIndex++) {
for (int selectorIndex = 0; selectorIndex < numSelectors; ++selectorIndex) {
if (method_getName(methods[methodIndex]) == selectors[selectorIndex]) {
[self injectIntoDelegateClass:class];
matchingSelectorFound = YES;
break;
}
}
if (matchingSelectorFound) {
break;
}
}
free(methods);
}
free(classes);
}
[self injectIntoNSURLConnectionCancel];
[self injectIntoNSURLSessionTaskResume];
[self injectIntoNSURLConnectionAsynchronousClassMethod];
[self injectIntoNSURLConnectionSynchronousClassMethod];
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods];
[self injectIntoNSURLSessionAsyncUploadTaskMethods];
if (@available(iOS 13.0, *)) {
Class websocketTask = NSClassFromString(@"__NSURLSessionWebSocketTask");
[self injectWebsocketSendMessage:websocketTask];
[self injectWebsocketReceiveMessage:websocketTask];
websocketTask = [NSURLSessionWebSocketTask class];
[self injectWebsocketSendMessage:websocketTask];
[self injectWebsocketReceiveMessage:websocketTask];
}
});
}
+ (void)injectIntoDelegateClass:(Class)cls {
// Connections
[self injectWillSendRequestIntoDelegateClass:cls];
[self injectDidReceiveDataIntoDelegateClass:cls];
[self injectDidReceiveResponseIntoDelegateClass:cls];
[self injectDidFinishLoadingIntoDelegateClass:cls];
[self injectDidFailWithErrorIntoDelegateClass:cls];
// Sessions
[self injectTaskWillPerformHTTPRedirectionIntoDelegateClass:cls];
[self injectTaskDidReceiveDataIntoDelegateClass:cls];
[self injectTaskDidReceiveResponseIntoDelegateClass:cls];
[self injectTaskDidCompleteWithErrorIntoDelegateClass:cls];
[self injectRespondsToSelectorIntoDelegateClass:cls];
// Data tasks
[self injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:cls];
// Download tasks
[self injectDownloadTaskDidWriteDataIntoDelegateClass:cls];
[self injectDownloadTaskDidFinishDownloadingIntoDelegateClass:cls];
}
+ (void)injectIntoNSURLConnectionCancel {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [NSURLConnection class];
SEL selector = @selector(cancel);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Method originalCancel = class_getInstanceMethod(class, selector);
void (^swizzleBlock)(NSURLConnection *) = ^(NSURLConnection *slf) {
[FLEXNetworkObserver.sharedObserver connectionWillCancel:slf];
((void(*)(id, SEL))objc_msgSend)(
slf, swizzledSelector
);
};
IMP implementation = imp_implementationWithBlock(swizzleBlock);
class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalCancel));
Method newCancel = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalCancel, newCancel);
});
}
+ (void)injectIntoNSURLSessionTaskResume {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// In iOS 7 resume lives in __NSCFLocalSessionTask
// In iOS 8 resume lives in NSURLSessionTask
// In iOS 9 resume lives in __NSCFURLSessionTask
// In iOS 14 resume lives in NSURLSessionTask
Class baseResumeClass = Nil;
if (![NSProcessInfo.processInfo respondsToSelector:@selector(operatingSystemVersion)]) {
// iOS ... 7
baseResumeClass = NSClassFromString(@"__NSCFLocalSessionTask");
} else {
NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion;
if (majorVersion < 9 || majorVersion >= 14) {
// iOS 8 or iOS 14+
baseResumeClass = [NSURLSessionTask class];
} else {
// iOS 9 ... 13
baseResumeClass = NSClassFromString(@"__NSCFURLSessionTask");
}
}
// Hook the base implementation of -resume
IMP originalResume = [baseResumeClass instanceMethodForSelector:@selector(resume)];
[self swizzleResumeSelector:@selector(resume) forClass:baseResumeClass];
// *Sigh*
//
// So, multiple versions of AFNetworking 2.5.X swizzle -resume in various and
// short-sighted ways. If you look through the version history from 2.5.0 upwards,
// you'll see a variety of techniques were tried, including taking a private
// subclass of NSURLSessionTask and calling class_addMethod with `originalResume`
// below, so that a duplicate implementation of -resume exists in that class.
//
// This technique in particular is troublesome, because the implementation in
// `baseResumeClass` is never called at all, which means our swizzle is never invoked.
//
// The only solution is a brute-force one: we must loop over the class tree
// below `baseResumeClass` and check for all classes that implement `af_resume`.
// if the IMP corresponding to that method is equal to `originalResume` then we
// swizzle that in addition to swizzling `resume` on `baseResumeClass` above.
//
// However, we only go to the trouble at all if NSSelectorFromString
// can even find an `"af_resume"` selector in the first place.
SEL sel_af_resume = NSSelectorFromString(@"af_resume");
if (sel_af_resume) {
NSMutableArray<Class> *classTree = FLEXGetAllSubclasses(baseResumeClass, NO).mutableCopy;
for (NSInteger i = 0; i < classTree.count; i++) {
[classTree addObjectsFromArray:FLEXGetAllSubclasses(classTree[i], NO)];
}
for (Class current in classTree) {
IMP af_resume = [current instanceMethodForSelector:sel_af_resume];
if (af_resume == originalResume) {
[self swizzleResumeSelector:sel_af_resume forClass:current];
}
}
}
});
}
+ (void)swizzleResumeSelector:(SEL)selector forClass:(Class)class {
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Method originalResume = class_getInstanceMethod(class, selector);
IMP implementation = imp_implementationWithBlock(^(NSURLSessionTask *slf) {
// iOS's internal HTTP parser finalization code is mysteriously not thread safe,
// invoking it asynchronously has a chance to cause a `double free` crash.
// This line below will ask for HTTPBody synchronously, make the HTTPParser
// parse the request, and cache them in advance. After that the HTTPParser
// will be finalized. Make sure other threads inspecting the request
// won't trigger a race to finalize the parser.
[slf.currentRequest HTTPBody];
[FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
((void(*)(id, SEL))objc_msgSend)(
slf, swizzledSelector
);
});
class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalResume));
Method newResume = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalResume, newResume);
}
+ (void)injectIntoNSURLConnectionAsynchronousClassMethod {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
SEL selector = @selector(sendAsynchronousRequest:queue:completionHandler:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
typedef void (^AsyncCompletion)(
NSURLResponse *response, NSData *data, NSError *error
);
typedef void (^SendAsyncRequestBlock)(
Class, NSURLRequest *, NSOperationQueue *, AsyncCompletion
);
SendAsyncRequestBlock swizzleBlock = ^(Class slf,
NSURLRequest *request,
NSOperationQueue *queue,
AsyncCompletion completion) {
if (FLEXNetworkObserver.isEnabled) {
NSString *requestID = [self nextRequestID];
[FLEXNetworkRecorder.defaultRecorder
recordRequestWillBeSentWithRequestID:requestID
request:request
redirectResponse:nil
];
NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
[FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
AsyncCompletion wrapper = ^(NSURLResponse *response, NSData *data, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:response
];
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:data.length
];
if (error) {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFailedWithRequestID:requestID
error:error
];
} else {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFinishedWithRequestID:requestID
responseBody:data
];
}
// Call through to the original completion handler
if (completion) {
completion(response, data, error);
}
};
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, request, queue, wrapper
);
} else {
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, request, queue, completion
);
}
};
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
];
});
}
+ (void)injectIntoNSURLConnectionSynchronousClassMethod {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
SEL selector = @selector(sendSynchronousRequest:returningResponse:error:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
typedef NSData * (^AsyncCompletion)(Class, NSURLRequest *, NSURLResponse **, NSError **);
AsyncCompletion swizzleBlock = ^NSData *(Class slf,
NSURLRequest *request,
NSURLResponse **response,
NSError **error) {
NSData *data = nil;
if (FLEXNetworkObserver.isEnabled) {
NSString *requestID = [self nextRequestID];
[FLEXNetworkRecorder.defaultRecorder
recordRequestWillBeSentWithRequestID:requestID
request:request
redirectResponse:nil
];
NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
[FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
NSError *temporaryError = nil;
NSURLResponse *temporaryResponse = nil;
data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)(
slf, swizzledSelector, request, &temporaryResponse, &temporaryError
);
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:temporaryResponse
];
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:data.length
];
if (temporaryError) {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFailedWithRequestID:requestID
error:temporaryError
];
} else {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFinishedWithRequestID:requestID
responseBody:data
];
}
if (error) {
*error = temporaryError;
}
if (response) {
*response = temporaryResponse;
}
} else {
data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)(
slf, swizzledSelector, request, response, error
);
}
return data;
};
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
];
});
}
+ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [NSURLSession class];
// The method signatures here are close enough that
// we can use the same logic to inject into all of them.
const SEL selectors[] = {
@selector(dataTaskWithRequest:completionHandler:),
@selector(dataTaskWithURL:completionHandler:),
@selector(downloadTaskWithRequest:completionHandler:),
@selector(downloadTaskWithResumeData:completionHandler:),
@selector(downloadTaskWithURL:completionHandler:)
};
const int numSelectors = sizeof(selectors) / sizeof(SEL);
for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
SEL selector = selectors[selectorIndex];
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
// iOS 7 does not implement these methods on NSURLSession. We actually want to
// swizzle __NSCFURLSession, which we can get from the class of the shared session
class = [NSURLSession.sharedSession class];
}
typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)(
NSURLSession *, id, NSURLSessionAsyncCompletion
);
NSURLSessionNewTaskMethod swizzleBlock = ^NSURLSessionTask *(NSURLSession *slf,
id argument,
NSURLSessionAsyncCompletion completion) {
NSURLSessionTask *task = nil;
// Check if network observing is on and a callback was provided
if (FLEXNetworkObserver.isEnabled && completion) {
NSString *requestID = [self nextRequestID];
NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
// "Hook" the completion block
NSURLSessionAsyncCompletion completionWrapper = [self
asyncCompletionWrapperForRequestID:requestID
mechanism:mechanism
completion:completion
];
// Call the original method
task = ((id(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, argument, completionWrapper
);
[self setRequestID:requestID forConnectionOrTask:task];
} else {
// Network observer disabled or no callback provided,
// just pass through to the original method
task = ((id(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, argument, completion
);
}
return task;
};
// Actually swizzle
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
];
}
});
}
+ (void)injectIntoNSURLSessionAsyncUploadTaskMethods {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [NSURLSession class];
// The method signatures here are close enough that we can use the same logic to inject into both of them.
// Note that they have 3 arguments, so we can't easily combine with the data and download method above.
typedef NSURLSessionUploadTask *(^UploadTaskMethod)(
NSURLSession *, NSURLRequest *, id, NSURLSessionAsyncCompletion
);
const SEL selectors[] = {
@selector(uploadTaskWithRequest:fromData:completionHandler:),
@selector(uploadTaskWithRequest:fromFile:completionHandler:)
};
const int numSelectors = sizeof(selectors) / sizeof(SEL);
for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
SEL selector = selectors[selectorIndex];
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
// iOS 7 does not implement these methods on NSURLSession. We actually want to
// swizzle __NSCFURLSession, which we can get from the class of the shared session
class = [NSURLSession.sharedSession class];
}
UploadTaskMethod swizzleBlock = ^NSURLSessionUploadTask *(NSURLSession * slf,
NSURLRequest *request,
id argument,
NSURLSessionAsyncCompletion completion) {
NSURLSessionUploadTask *task = nil;
if (FLEXNetworkObserver.isEnabled && completion) {
NSString *requestID = [self nextRequestID];
NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class];
NSURLSessionAsyncCompletion completionWrapper = [self
asyncCompletionWrapperForRequestID:requestID
mechanism:mechanism
completion:completion
];
task = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, request, argument, completionWrapper
);
[self setRequestID:requestID forConnectionOrTask:task];
} else {
task = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, request, argument, completion
);
}
return task;
};
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector
];
}
});
}
+ (NSString *)mechanismFromClassMethod:(SEL)selector onClass:(Class)class {
return [NSString stringWithFormat:@"+[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)];
}
+ (NSURLSessionAsyncCompletion)asyncCompletionWrapperForRequestID:(NSString *)requestID
mechanism:(NSString *)mechanism
completion:(NSURLSessionAsyncCompletion)completion {
NSURLSessionAsyncCompletion completionWrapper = ^(id fileURLOrData, NSURLResponse *response, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:response
];
NSData *data = nil;
if ([fileURLOrData isKindOfClass:[NSURL class]]) {
data = [NSData dataWithContentsOfURL:fileURLOrData];
} else if ([fileURLOrData isKindOfClass:[NSData class]]) {
data = fileURLOrData;
}
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:data.length
];
if (error) {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFailedWithRequestID:requestID
error:error
];
} else {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFinishedWithRequestID:requestID
responseBody:data
];
}
// Call through to the original completion handler
if (completion) {
completion(fileURLOrData, response, error);
}
};
return completionWrapper;
}
+ (void)injectWillSendRequestIntoDelegateClass:(Class)cls {
SEL selector = @selector(connection:willSendRequest:redirectResponse:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
protocol = protocol ?: @protocol(NSURLConnectionDelegate);
struct objc_method_description methodDescription = protocol_getMethodDescription(
protocol, selector, NO, YES
);
typedef NSURLRequest *(^WillSendRequestBlock)(
id<NSURLConnectionDelegate> slf, NSURLConnection *connection,
NSURLRequest *request, NSURLResponse *response
);
WillSendRequestBlock undefinedBlock = ^NSURLRequest *(id slf,
NSURLConnection *connection,
NSURLRequest *request,
NSURLResponse *response) {
[FLEXNetworkObserver.sharedObserver
connection:connection
willSendRequest:request
redirectResponse:response
delegate:slf
];
return request;
};
WillSendRequestBlock implementationBlock = ^NSURLRequest *(id slf,
NSURLConnection *connection,
NSURLRequest *request,
NSURLResponse *response) {
__block NSURLRequest *returnValue = nil;
[self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
undefinedBlock(slf, connection, request, response);
} originalImplementationBlock:^{
returnValue = ((id(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, connection, request, response
);
}];
return returnValue;
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:methodDescription
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDidReceiveResponseIntoDelegateClass:(Class)cls {
SEL selector = @selector(connection:didReceiveResponse:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
protocol = protocol ?: @protocol(NSURLConnectionDelegate);
struct objc_method_description description = protocol_getMethodDescription(
protocol, selector, NO, YES
);
typedef void (^DidReceiveResponseBlock)(
id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSURLResponse *response
);
DidReceiveResponseBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSURLResponse *response) {
[FLEXNetworkObserver.sharedObserver connection:connection
didReceiveResponse:response delegate:slf
];
};
DidReceiveResponseBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSURLResponse *response) {
[self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
undefinedBlock(slf, connection, response);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, connection, response
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDidReceiveDataIntoDelegateClass:(Class)cls {
SEL selector = @selector(connection:didReceiveData:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
protocol = protocol ?: @protocol(NSURLConnectionDelegate);
struct objc_method_description description = protocol_getMethodDescription(
protocol, selector, NO, YES
);
typedef void (^DidReceiveDataBlock)(
id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSData *data
);
DidReceiveDataBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSData *data) {
[FLEXNetworkObserver.sharedObserver connection:connection
didReceiveData:data delegate:slf
];
};
DidReceiveDataBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSData *data) {
[self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
undefinedBlock(slf, connection, data);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, connection, data
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDidFinishLoadingIntoDelegateClass:(Class)cls {
SEL selector = @selector(connectionDidFinishLoading:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
protocol = protocol ?: @protocol(NSURLConnectionDelegate);
struct objc_method_description description = protocol_getMethodDescription(
protocol, selector, NO, YES
);
typedef void (^FinishLoadingBlock)(id<NSURLConnectionDelegate> slf, NSURLConnection *connection);
FinishLoadingBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf, NSURLConnection *connection) {
[FLEXNetworkObserver.sharedObserver connectionDidFinishLoading:connection delegate:slf];
};
FinishLoadingBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf, NSURLConnection *connection) {
[self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
undefinedBlock(slf, connection);
} originalImplementationBlock:^{
((void(*)(id, SEL, id))objc_msgSend)(
slf, swizzledSelector, connection
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDidFailWithErrorIntoDelegateClass:(Class)cls {
SEL selector = @selector(connection:didFailWithError:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLConnectionDelegate), selector, NO, YES
);
typedef void (^DidFailWithErrorBlock)(
id<NSURLConnectionDelegate> slf, NSURLConnection *connection, NSError *error
);
DidFailWithErrorBlock undefinedBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSError *error) {
[FLEXNetworkObserver.sharedObserver connection:connection
didFailWithError:error delegate:slf
];
};
DidFailWithErrorBlock implementationBlock = ^(id<NSURLConnectionDelegate> slf,
NSURLConnection *connection,
NSError *error) {
[self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{
undefinedBlock(slf, connection, error);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, connection, error
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectTaskWillPerformHTTPRedirectionIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionTaskDelegate), selector, NO, YES
);
typedef void (^HTTPRedirectionBlock)(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSHTTPURLResponse *response,
NSURLRequest *newRequest,
void(^completionHandler)(NSURLRequest *));
HTTPRedirectionBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSHTTPURLResponse *response,
NSURLRequest *newRequest,
void(^completionHandler)(NSURLRequest *)) {
[FLEXNetworkObserver.sharedObserver
URLSession:session task:task
willPerformHTTPRedirection:response
newRequest:newRequest
completionHandler:completionHandler
delegate:slf
];
completionHandler(newRequest);
};
HTTPRedirectionBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSHTTPURLResponse *response,
NSURLRequest *newRequest,
void(^completionHandler)(NSURLRequest *)) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
[FLEXNetworkObserver.sharedObserver
URLSession:session task:task
willPerformHTTPRedirection:response
newRequest:newRequest
completionHandler:completionHandler
delegate:slf
];
} originalImplementationBlock:^{
((id(*)(id, SEL, id, id, id, id, void(^)(NSURLRequest *)))objc_msgSend)(
slf, swizzledSelector, session, task, response, newRequest, completionHandler
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectTaskDidReceiveDataIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:dataTask:didReceiveData:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDataDelegate), selector, NO, YES
);
typedef void (^DidReceiveDataBlock)(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSData *data);
DidReceiveDataBlock undefinedBlock = ^(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSData *data) {
[FLEXNetworkObserver.sharedObserver URLSession:session
dataTask:dataTask didReceiveData:data delegate:slf
];
};
DidReceiveDataBlock implementationBlock = ^(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSData *data) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
undefinedBlock(slf, session, dataTask, data);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, session, dataTask, data
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:dataTask:didBecomeDownloadTask:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDataDelegate), selector, NO, YES
);
typedef void (^DidBecomeDownloadTaskBlock)(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLSessionDownloadTask *downloadTask);
DidBecomeDownloadTaskBlock undefinedBlock = ^(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLSessionDownloadTask *downloadTask) {
[FLEXNetworkObserver.sharedObserver URLSession:session
dataTask:dataTask didBecomeDownloadTask:downloadTask delegate:slf
];
};
DidBecomeDownloadTaskBlock implementationBlock = ^(id<NSURLSessionDataDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLSessionDownloadTask *downloadTask) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
undefinedBlock(slf, session, dataTask, downloadTask);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, session, dataTask, downloadTask
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectTaskDidReceiveResponseIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:dataTask:didReceiveResponse:completionHandler:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDataDelegate), selector, NO, YES
);
typedef void (^DidReceiveResponseBlock)(id<NSURLSessionDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLResponse *response,
void(^completion)(NSURLSessionResponseDisposition));
DidReceiveResponseBlock undefinedBlock = ^(id<NSURLSessionDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLResponse *response,
void(^completion)(NSURLSessionResponseDisposition)) {
[FLEXNetworkObserver.sharedObserver
URLSession:session
dataTask:dataTask
didReceiveResponse:response
completionHandler:completion
delegate:slf
];
completion(NSURLSessionResponseAllow);
};
DidReceiveResponseBlock implementationBlock = ^(id<NSURLSessionDelegate> slf,
NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSURLResponse *response,
void(^completion)(NSURLSessionResponseDisposition )) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
[FLEXNetworkObserver.sharedObserver
URLSession:session
dataTask:dataTask
didReceiveResponse:response
completionHandler:completion
delegate:slf
];
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, id, void(^)(NSURLSessionResponseDisposition)))objc_msgSend)(
slf, swizzledSelector, session, dataTask, response, completion
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectTaskDidCompleteWithErrorIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:task:didCompleteWithError:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDataDelegate), selector, NO, YES
);
typedef void (^DidCompleteWithErrorBlock)(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSError *error);
DidCompleteWithErrorBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSError *error) {
[FLEXNetworkObserver.sharedObserver URLSession:session
task:task didCompleteWithError:error delegate:slf
];
};
DidCompleteWithErrorBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionTask *task,
NSError *error) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
undefinedBlock(slf, session, task, error);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, session, task, error
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
// Used for overriding AFNetworking behavior
+ (void)injectRespondsToSelectorIntoDelegateClass:(Class)cls {
SEL selector = @selector(respondsToSelector:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
//Protocol *protocol = @protocol(NSURLSessionTaskDelegate);
Method method = class_getInstanceMethod(cls, selector);
struct objc_method_description methodDescription = *method_getDescription(method);
typedef BOOL (^RespondsToSelectorImpl)(id self, SEL sel);
RespondsToSelectorImpl undefinedBlock = ^(id slf, SEL sel) {
return YES;
};
RespondsToSelectorImpl implementationBlock = ^(id<NSURLSessionTaskDelegate> slf, SEL sel) {
if (sel == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
return undefinedBlock(slf, sel);
}
return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(slf, swizzledSelector, sel);
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:methodDescription
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDownloadTaskDidFinishDownloadingIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:downloadTask:didFinishDownloadingToURL:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDownloadDelegate), selector, NO, YES
);
typedef void (^DidFinishDownloadingBlock)(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
NSURL *location);
DidFinishDownloadingBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
NSURL *location) {
NSData *data = [NSData dataWithContentsOfFile:location.relativePath];
[FLEXNetworkObserver.sharedObserver URLSession:session
task:task didFinishDownloadingToURL:location data:data delegate:slf
];
};
DidFinishDownloadingBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
NSURL *location) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
undefinedBlock(slf, session, task, location);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, id))objc_msgSend)(
slf, swizzledSelector, session, task, location
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectDownloadTaskDidWriteDataIntoDelegateClass:(Class)cls {
SEL selector = @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
struct objc_method_description description = protocol_getMethodDescription(
@protocol(NSURLSessionDownloadDelegate), selector, NO, YES
);
typedef void (^DidWriteDataBlock)(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
int64_t bytesWritten,
int64_t totalBytesWritten,
int64_t totalBytesExpectedToWrite);
DidWriteDataBlock undefinedBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
int64_t bytesWritten,
int64_t totalBytesWritten,
int64_t totalBytesExpectedToWrite) {
[FLEXNetworkObserver.sharedObserver URLSession:session
downloadTask:task didWriteData:bytesWritten
totalBytesWritten:totalBytesWritten
totalBytesExpectedToWrite:totalBytesExpectedToWrite
delegate:slf
];
};
DidWriteDataBlock implementationBlock = ^(id<NSURLSessionTaskDelegate> slf,
NSURLSession *session,
NSURLSessionDownloadTask *task,
int64_t bytesWritten,
int64_t totalBytesWritten,
int64_t totalBytesExpectedToWrite) {
[self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{
undefinedBlock(
slf, session, task, bytesWritten,
totalBytesWritten, totalBytesExpectedToWrite
);
} originalImplementationBlock:^{
((void(*)(id, SEL, id, id, int64_t, int64_t, int64_t))objc_msgSend)(
slf, swizzledSelector, session, task, bytesWritten,
totalBytesWritten, totalBytesExpectedToWrite
);
}];
};
[FLEXUtility replaceImplementationOfSelector:selector
withSelector:swizzledSelector
forClass:cls
withMethodDescription:description
implementationBlock:implementationBlock
undefinedBlock:undefinedBlock
];
}
+ (void)injectWebsocketSendMessage:(Class)cls API_AVAILABLE(ios(13.0)) {
SEL selector = @selector(sendMessage:completionHandler:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
typedef void (^SendMessageBlock)(
NSURLSessionWebSocketTask *slf,
NSURLSessionWebSocketMessage *message,
void (^completion)(NSError *error)
);
SendMessageBlock implementationBlock = ^(
NSURLSessionWebSocketTask *slf,
NSURLSessionWebSocketMessage *message,
void (^completion)(NSError *error)
) {
[FLEXNetworkObserver.sharedObserver
websocketTask:slf sendMessagage:message
];
completion = ^(NSError *error) {
[FLEXNetworkObserver.sharedObserver
websocketTaskMessageSendCompletion:message
error:error
];
};
((void(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, message, completion
);
};
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:cls
withBlock:implementationBlock
swizzledSelector:swizzledSelector
];
}
+ (void)injectWebsocketReceiveMessage:(Class)cls API_AVAILABLE(ios(13.0)) {
SEL selector = @selector(receiveMessageWithCompletionHandler:);
SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
typedef void (^SendMessageBlock)(
NSURLSessionWebSocketTask *slf,
void (^completion)(NSURLSessionWebSocketMessage *message, NSError *error)
);
SendMessageBlock implementationBlock = ^(
NSURLSessionWebSocketTask *slf,
void (^completion)(NSURLSessionWebSocketMessage *message, NSError *error)
) {
id completionHook = ^(NSURLSessionWebSocketMessage *message, NSError *error) {
[FLEXNetworkObserver.sharedObserver
websocketTask:slf receiveMessagage:message error:error
];
completion(message, error);
};
((void(*)(id, SEL, id))objc_msgSend)(
slf, swizzledSelector, completionHook
);
};
[FLEXUtility replaceImplementationOfKnownSelector:selector
onClass:cls
withBlock:implementationBlock
swizzledSelector:swizzledSelector
];
}
static char const * const kFLEXRequestIDKey = "kFLEXRequestIDKey";
+ (NSString *)requestIDForConnectionOrTask:(id)connectionOrTask {
NSString *requestID = objc_getAssociatedObject(connectionOrTask, kFLEXRequestIDKey);
if (!requestID) {
requestID = [self nextRequestID];
[self setRequestID:requestID forConnectionOrTask:connectionOrTask];
}
return requestID;
}
+ (void)setRequestID:(NSString *)requestID forConnectionOrTask:(id)connectionOrTask {
objc_setAssociatedObject(
connectionOrTask, kFLEXRequestIDKey, requestID, OBJC_ASSOCIATION_RETAIN_NONATOMIC
);
}
#pragma mark - Initialization
- (id)init {
self = [super init];
if (self) {
self.requestStatesForRequestIDs = [NSMutableDictionary new];
self.queue = dispatch_queue_create(
"com.flex.FLEXNetworkObserver", DISPATCH_QUEUE_SERIAL
);
}
return self;
}
#pragma mark - Private Methods
- (void)performBlock:(dispatch_block_t)block {
if ([[self class] isEnabled]) {
dispatch_async(_queue, block);
}
}
- (FLEXInternalRequestState *)requestStateForRequestID:(NSString *)requestID {
FLEXInternalRequestState *requestState = self.requestStatesForRequestIDs[requestID];
if (!requestState) {
requestState = [FLEXInternalRequestState new];
[self.requestStatesForRequestIDs setObject:requestState forKey:requestID];
}
return requestState;
}
- (void)removeRequestStateForRequestID:(NSString *)requestID {
[self.requestStatesForRequestIDs removeObjectForKey:requestID];
}
@end
@implementation FLEXNetworkObserver (NSURLConnectionHelpers)
- (void)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)response
delegate:(id<NSURLConnectionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
requestState.request = request;
[FLEXNetworkRecorder.defaultRecorder
recordRequestWillBeSentWithRequestID:requestID
request:request
redirectResponse:response
];
NSString *mechanism = [NSString stringWithFormat:
@"NSURLConnection (delegate: %@)", [delegate class]
];
[FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID];
}];
}
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
delegate:(id<NSURLConnectionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
requestState.dataAccumulator = [NSMutableData new];
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:response
];
}];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
delegate:(id<NSURLConnectionDelegate>)delegate {
// Just to be safe since we're doing this async
data = [data copy];
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
[requestState.dataAccumulator appendData:data];
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:data.length
];
}];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
delegate:(id<NSURLConnectionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFinishedWithRequestID:requestID
responseBody:requestState.dataAccumulator
];
[self removeRequestStateForRequestID:requestID];
}];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
delegate:(id<NSURLConnectionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:connection];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
// Cancellations can occur prior to the willSendRequest:...
// NSURLConnection delegate call. These are pretty common
// and clutter up the logs. Only record the failure if the
// recorder already knows about the request through willSendRequest:...
if (requestState.request) {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFailedWithRequestID:requestID error:error
];
}
[self removeRequestStateForRequestID:requestID];
}];
}
- (void)connectionWillCancel:(NSURLConnection *)connection {
[self performBlock:^{
// Mimic the behavior of NSURLSession which is to create an error on cancellation.
NSDictionary<NSString *, id> *userInfo = @{ NSLocalizedDescriptionKey : @"cancelled" };
NSError *error = [NSError errorWithDomain:NSURLErrorDomain
code:NSURLErrorCancelled userInfo:userInfo
];
[self connection:connection didFailWithError:error delegate:nil];
}];
}
@end
@implementation FLEXNetworkObserver (NSURLSessionTaskHelpers)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
delegate:(id<NSURLSessionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
[FLEXNetworkRecorder.defaultRecorder
recordRequestWillBeSentWithRequestID:requestID
request:request
redirectResponse:response
];
}];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
delegate:(id<NSURLSessionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
requestState.dataAccumulator = [NSMutableData new];
NSString *requestMechanism = [NSString stringWithFormat:
@"NSURLSessionDataTask (delegate: %@)", [delegate class]
];
[FLEXNetworkRecorder.defaultRecorder
recordMechanism:requestMechanism
forRequestID:requestID
];
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:response
];
}];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
delegate:(id<NSURLSessionDelegate>)delegate {
[self performBlock:^{
// By setting the request ID of the download task to match the data task,
// it can pick up where the data task left off.
NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
[[self class] setRequestID:requestID forConnectionOrTask:downloadTask];
}];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
delegate:(id<NSURLSessionDelegate>)delegate {
// Just to be safe since we're doing this async
data = [data copy];
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
[requestState.dataAccumulator appendData:data];
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:data.length
];
}];
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
delegate:(id<NSURLSessionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
if (error) {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFailedWithRequestID:requestID error:error
];
} else {
[FLEXNetworkRecorder.defaultRecorder
recordLoadingFinishedWithRequestID:requestID
responseBody:requestState.dataAccumulator
];
}
[self removeRequestStateForRequestID:requestID];
}];
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
delegate:(id<NSURLSessionDelegate>)delegate {
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
if (!requestState.dataAccumulator) {
requestState.dataAccumulator = [NSMutableData new];
[FLEXNetworkRecorder.defaultRecorder
recordResponseReceivedWithRequestID:requestID
response:downloadTask.response
];
NSString *requestMechanism = [NSString stringWithFormat:
@"NSURLSessionDownloadTask (delegate: %@)", [delegate class]
];
[FLEXNetworkRecorder.defaultRecorder
recordMechanism:requestMechanism
forRequestID:requestID
];
}
[FLEXNetworkRecorder.defaultRecorder
recordDataReceivedWithRequestID:requestID
dataLength:bytesWritten
];
}];
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data
delegate:(id<NSURLSessionDelegate>)delegate {
data = [data copy];
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
[requestState.dataAccumulator appendData:data];
}];
}
- (void)URLSessionTaskWillResume:(NSURLSessionTask *)task {
// Since resume can be called multiple times on the same task, only treat the first resume as
// the equivalent to connection:willSendRequest:...
[self performBlock:^{
NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
if (!requestState.request) {
requestState.request = task.currentRequest;
[FLEXNetworkRecorder.defaultRecorder
recordRequestWillBeSentWithRequestID:requestID
request:task.currentRequest
redirectResponse:nil
];
}
}];
}
- (void)websocketTask:(NSURLSessionWebSocketTask *)task
sendMessagage:(NSURLSessionWebSocketMessage *)message {
[self performBlock:^{
// NSString *requestID = [[self class] requestIDForConnectionOrTask:task];
[FLEXNetworkRecorder.defaultRecorder recordWebsocketMessageSend:message task:task];
}];
}
- (void)websocketTaskMessageSendCompletion:(NSURLSessionWebSocketMessage *)message
error:(NSError *)error {
[self performBlock:^{
[FLEXNetworkRecorder.defaultRecorder
recordWebsocketMessageSendCompletion:message
error:error
];
}];
}
- (void)websocketTask:(NSURLSessionWebSocketTask *)task
receiveMessagage:(NSURLSessionWebSocketMessage *)message
error:(NSError *)error {
[self performBlock:^{
if (!error && message) {
[FLEXNetworkRecorder.defaultRecorder
recordWebsocketMessageReceived:message
task:task
];
}
}];
}
@end

View File

@@ -0,0 +1,16 @@
PonyDebugger
Copyright 2012 Square Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.