mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
308 lines
14 KiB
Objective-C
308 lines
14 KiB
Objective-C
// Copyright 2020 Google LLC
|
|
//
|
|
// 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.
|
|
|
|
#import "FirebasePerformance/Sources/Instrumentation/Network/Delegates/FPRNSURLConnectionDelegateInstrument.h"
|
|
|
|
#import "FirebasePerformance/Sources/Instrumentation/FPRClassInstrumentor.h"
|
|
#import "FirebasePerformance/Sources/Instrumentation/FPRInstrument_Private.h"
|
|
#import "FirebasePerformance/Sources/Instrumentation/FPRNetworkTrace.h"
|
|
#import "FirebasePerformance/Sources/Instrumentation/FPRSelectorInstrumentor.h"
|
|
#import "FirebasePerformance/Sources/Instrumentation/Network/Delegates/FPRNSURLConnectionDelegate.h"
|
|
#import "FirebasePerformance/Sources/Instrumentation/Network/FPRNetworkInstrumentHelpers.h"
|
|
|
|
#pragma mark - NSURLConnectionDelegate methods
|
|
|
|
/** Returns the dispatch queue for all instrumentation to occur on. */
|
|
static dispatch_queue_t GetInstrumentationQueue(void) {
|
|
static dispatch_queue_t queue;
|
|
static dispatch_once_t token;
|
|
dispatch_once(&token, ^{
|
|
queue = dispatch_queue_create("com.google.FPRNSURLConnectionDelegateInstrument",
|
|
DISPATCH_QUEUE_SERIAL);
|
|
});
|
|
return queue;
|
|
}
|
|
|
|
/** Instruments connection:didFailWithError:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidFailWithError(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:didFailWithError:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, NSError *error) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
[trace didCompleteRequestWithResponse:nil error:error];
|
|
[FPRNetworkTrace removeNetworkTraceFromObject:connection];
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, NSError *);
|
|
((OriginalImp)currentIMP)(object, selector, connection, error);
|
|
}];
|
|
}
|
|
}
|
|
|
|
#pragma mark - NSURLConnectionDataDelegate methods
|
|
|
|
/** Instruments connection:willSendRequest:redirectResponse:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionWillSendRequestRedirectResponse(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:willSendRequest:redirectResponse:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor setReplacingBlock:^(id object, NSURLConnection *connection,
|
|
NSURLRequest *request, NSURLResponse *response) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
[trace checkpointState:FPRNetworkTraceCheckpointStateResponseReceived];
|
|
typedef NSURLRequest *(*OriginalImp)(id, SEL, NSURLConnection *, NSURLRequest *,
|
|
NSURLResponse *);
|
|
return ((OriginalImp)currentIMP)(object, selector, connection, request, response);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connection:didReceiveResponse:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidReceiveResponse(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:didReceiveResponse:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, NSURLResponse *response) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
|
trace.responseCode = (int32_t)((NSHTTPURLResponse *)response).statusCode;
|
|
}
|
|
[trace checkpointState:FPRNetworkTraceCheckpointStateResponseReceived];
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, NSURLResponse *);
|
|
((OriginalImp)currentIMP)(object, selector, connection, response);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connection:didReceiveData:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidReceiveData(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:didReceiveData:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, NSData *data) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
[trace checkpointState:FPRNetworkTraceCheckpointStateResponseReceived];
|
|
trace.responseSize += data.length;
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, NSData *);
|
|
((OriginalImp)currentIMP)(object, selector, connection, data);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionAllTheTotals(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, NSInteger bytesWritten,
|
|
NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
trace.requestSize = totalBytesWritten;
|
|
if (totalBytesWritten >= totalBytesExpectedToWrite) {
|
|
[trace checkpointState:FPRNetworkTraceCheckpointStateRequestCompleted];
|
|
}
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, NSInteger, NSInteger, NSInteger);
|
|
((OriginalImp)currentIMP)(object, selector, connection, bytesWritten, totalBytesWritten,
|
|
totalBytesExpectedToWrite);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connectionDidFinishLoading:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidFinishLoading(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connectionDidFinishLoading:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor setReplacingBlock:^(id object, NSURLConnection *connection) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
[trace didCompleteRequestWithResponse:nil error:nil];
|
|
[FPRNetworkTrace removeNetworkTraceFromObject:connection];
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *);
|
|
((OriginalImp)currentIMP)(object, selector, connection);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connection:didWriteData:totalBytesWritten:expectedTotalBytes:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidWriteDataTotalBytesWrittenExpectedTotalBytes(
|
|
FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connection:didWriteData:totalBytesWritten:expectedTotalBytes:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, long long bytesWritten,
|
|
long long totalBytesWritten, long long expectedTotalBytes) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
trace.requestSize = totalBytesWritten;
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, long long, long long, long long);
|
|
((OriginalImp)currentIMP)(object, selector, connection, bytesWritten, totalBytesWritten,
|
|
expectedTotalBytes);
|
|
}];
|
|
}
|
|
}
|
|
|
|
/** Instruments connectionDidFinishDownloading:destinationURL:.
|
|
*
|
|
* @param instrumentor The FPRClassInstrumentor to add the selector instrumentor to.
|
|
*/
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void InstrumentConnectionDidFinishDownloadingDestinationURL(FPRClassInstrumentor *instrumentor) {
|
|
SEL selector = @selector(connectionDidFinishDownloading:destinationURL:);
|
|
FPRSelectorInstrumentor *selectorInstrumentor =
|
|
[instrumentor instrumentorForInstanceSelector:selector];
|
|
if (selectorInstrumentor) {
|
|
IMP currentIMP = selectorInstrumentor.currentIMP;
|
|
[selectorInstrumentor
|
|
setReplacingBlock:^(id object, NSURLConnection *connection, NSURL *destinationURL) {
|
|
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:connection];
|
|
[trace didReceiveFileURL:destinationURL];
|
|
[trace didCompleteRequestWithResponse:nil error:nil];
|
|
[FPRNetworkTrace removeNetworkTraceFromObject:connection];
|
|
typedef void (*OriginalImp)(id, SEL, NSURLConnection *, NSURL *);
|
|
((OriginalImp)currentIMP)(object, selector, connection, destinationURL);
|
|
}];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Helper functions
|
|
FOUNDATION_STATIC_INLINE
|
|
NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
|
|
void CopySelector(SEL selector, FPRObjectInstrumentor *instrumentor) {
|
|
static Class fromClass = Nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
fromClass = [FPRNSURLConnectionDelegate class];
|
|
});
|
|
if (![instrumentor.instrumentedObject respondsToSelector:selector]) {
|
|
[instrumentor copySelector:selector fromClass:fromClass isClassSelector:NO];
|
|
}
|
|
}
|
|
|
|
#pragma mark - FPRNSURLConnectionDelegateInstrument
|
|
|
|
@implementation FPRNSURLConnectionDelegateInstrument
|
|
|
|
- (void)registerInstrumentors {
|
|
// Do nothing by default. classes will be instrumented on-demand upon discovery.
|
|
}
|
|
|
|
- (void)registerClass:(Class)aClass {
|
|
dispatch_sync(GetInstrumentationQueue(), ^{
|
|
// If this class has already been instrumented, just return.
|
|
FPRClassInstrumentor *instrumentor = [[FPRClassInstrumentor alloc] initWithClass:aClass];
|
|
if (![self registerClassInstrumentor:instrumentor]) {
|
|
return;
|
|
}
|
|
|
|
InstrumentConnectionDidFailWithError(instrumentor);
|
|
InstrumentConnectionWillSendRequestRedirectResponse(instrumentor);
|
|
InstrumentConnectionDidReceiveResponse(instrumentor);
|
|
InstrumentConnectionDidReceiveData(instrumentor);
|
|
InstrumentConnectionAllTheTotals(instrumentor);
|
|
InstrumentConnectionDidFinishLoading(instrumentor);
|
|
InstrumentConnectionDidWriteDataTotalBytesWrittenExpectedTotalBytes(instrumentor);
|
|
InstrumentConnectionDidFinishDownloadingDestinationURL(instrumentor);
|
|
|
|
[instrumentor swizzle];
|
|
});
|
|
}
|
|
|
|
- (void)registerObject:(id)object {
|
|
dispatch_sync(GetInstrumentationQueue(), ^{
|
|
if ([object respondsToSelector:@selector(gul_class)]) {
|
|
return;
|
|
}
|
|
|
|
if (![self isObjectInstrumentable:object]) {
|
|
return;
|
|
}
|
|
|
|
FPRObjectInstrumentor *instrumentor = [[FPRObjectInstrumentor alloc] initWithObject:object];
|
|
|
|
// Register the non-swizzled versions of these methods.
|
|
CopySelector(@selector(connection:didFailWithError:), instrumentor);
|
|
CopySelector(@selector(connection:willSendRequest:redirectResponse:), instrumentor);
|
|
CopySelector(@selector(connection:didReceiveResponse:), instrumentor);
|
|
CopySelector(@selector(connection:didReceiveData:), instrumentor);
|
|
CopySelector(@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:),
|
|
instrumentor);
|
|
if (![object respondsToSelector:@selector(connectionDidFinishDownloading:destinationURL:)]) {
|
|
CopySelector(@selector(connectionDidFinishLoading:), instrumentor);
|
|
}
|
|
|
|
CopySelector(@selector(connection:didWriteData:totalBytesWritten:expectedTotalBytes:),
|
|
instrumentor);
|
|
if (![object respondsToSelector:@selector(connectionDidFinishLoading:)]) {
|
|
CopySelector(@selector(connectionDidFinishDownloading:destinationURL:), instrumentor);
|
|
}
|
|
|
|
[instrumentor swizzle];
|
|
});
|
|
}
|
|
|
|
@end
|