|
|
//
|
|
|
// NSURLConnection+AutoTrack.m
|
|
|
// YHEventReport
|
|
|
//
|
|
|
// Created by 孙凯 on 2017/9/30.
|
|
|
// Copyright © 2017年 YOHO. All rights reserved.
|
|
|
//
|
|
|
|
|
|
#import "NSURLConnection+AutoTrack.h"
|
|
|
#import "YHSwizzle.h"
|
|
|
#import <objc/runtime.h>
|
|
|
#import <objc/message.h>
|
|
|
#import "YHEventReport.h"
|
|
|
#import "YHLog.h"
|
|
|
#import "YH_EventCollector.h"
|
|
|
|
|
|
@interface NSURLConnection()
|
|
|
|
|
|
@property(nullable,nonatomic,weak) id my_delegate;
|
|
|
|
|
|
@property (readonly, copy) NSURLResponse *my_response;
|
|
|
|
|
|
@end
|
|
|
|
|
|
@implementation NSURLConnection (AutoTrack)
|
|
|
|
|
|
#pragma mark - 属性
|
|
|
- (id)my_delegate
|
|
|
{
|
|
|
return objc_getAssociatedObject(self, _cmd);
|
|
|
}
|
|
|
|
|
|
- (void)setMy_delegate:(id)my_delegate
|
|
|
{
|
|
|
objc_setAssociatedObject(self, @selector(my_delegate), my_delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
}
|
|
|
|
|
|
- (id)my_response
|
|
|
{
|
|
|
return objc_getAssociatedObject(self, _cmd);
|
|
|
}
|
|
|
|
|
|
- (void)setMy_response:(id)my_response
|
|
|
{
|
|
|
objc_setAssociatedObject(self, @selector(my_response), my_response, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
}
|
|
|
|
|
|
|
|
|
+(void)startTrack{
|
|
|
static dispatch_once_t onceToken;
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
@try {
|
|
|
|
|
|
SEL selectors[] = {
|
|
|
@selector(initWithRequest:delegate:),
|
|
|
@selector(initWithRequest:delegate:startImmediately:),
|
|
|
@selector(start),
|
|
|
};
|
|
|
|
|
|
for (NSUInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
|
|
|
SEL originalSelector = selectors[index];
|
|
|
SEL swizzledSelector = NSSelectorFromString([@"yher_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
|
|
|
NSError *error = NULL;
|
|
|
[[self class] yh_swizzleMethod:originalSelector
|
|
|
withMethod:swizzledSelector
|
|
|
error:&error];
|
|
|
if (error) {
|
|
|
YHLog(@"Failed to swizzle: on UIControl. Details: %@", error);
|
|
|
error = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
SEL class_selectors[] = {
|
|
|
@selector(sendSynchronousRequest:returningResponse:error:),
|
|
|
@selector(sendAsynchronousRequest:queue:completionHandler:),
|
|
|
};
|
|
|
|
|
|
for (NSUInteger index = 0; index < sizeof(class_selectors) / sizeof(SEL); ++index) {
|
|
|
Class selfClass = object_getClass([self class]);
|
|
|
SEL oriSEL = class_selectors[index];
|
|
|
Method oriMethod = class_getClassMethod(selfClass, oriSEL);
|
|
|
SEL cusSEL = NSSelectorFromString([@"yher_" stringByAppendingString:NSStringFromSelector(oriSEL)]);
|
|
|
Method cusMethod = class_getClassMethod(selfClass, cusSEL);
|
|
|
|
|
|
BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
|
|
|
if (addSucc) {
|
|
|
class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
|
|
|
}else {
|
|
|
method_exchangeImplementations(oriMethod, cusMethod);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
- (nullable instancetype)yher_initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate startImmediately:(BOOL)startImmediately
|
|
|
{
|
|
|
[self setMy_delegate:delegate];
|
|
|
return [self yher_initWithRequest:request delegate:self startImmediately:startImmediately];
|
|
|
}
|
|
|
|
|
|
- (BOOL)respondsToSelector:(SEL)aSelector
|
|
|
{
|
|
|
if ([super respondsToSelector:aSelector])
|
|
|
return YES;
|
|
|
|
|
|
if ([self.my_delegate respondsToSelector:aSelector])
|
|
|
return YES;
|
|
|
|
|
|
return NO;
|
|
|
}
|
|
|
|
|
|
- (id)forwardingTargetForSelector:(SEL)aSelector
|
|
|
{
|
|
|
if ([self.my_delegate respondsToSelector:aSelector])
|
|
|
return self.my_delegate;
|
|
|
|
|
|
return [super forwardingTargetForSelector:aSelector];
|
|
|
}
|
|
|
|
|
|
- (nullable instancetype)yher_initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate
|
|
|
{
|
|
|
[self setMy_delegate:delegate];
|
|
|
return [self yher_initWithRequest:request delegate:self];
|
|
|
}
|
|
|
|
|
|
- (void)yher_start
|
|
|
{
|
|
|
@try {
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable) {
|
|
|
[[YH_EventCollector sharedInstance] timeEventStartWithRequestUrl:[self.currentRequest.URL absoluteString]];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
|
|
|
[self yher_start];
|
|
|
}
|
|
|
|
|
|
+ (nullable NSData *)yher_sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse * _Nullable * _Nullable)response error:(NSError **)error
|
|
|
{
|
|
|
NSString *hostInHTTPField = [request.allHTTPHeaderFields objectForKey:@"Host"];
|
|
|
|
|
|
@try {
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable && ([[request.URL host] isEqualToString:YHEventReportHttpHost] || [hostInHTTPField isEqualToString:YHEventReportHttpHost])) {
|
|
|
[[YH_EventCollector sharedInstance] timeEventStartWithRequestUrl:[request.URL absoluteString]];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
|
|
|
NSData *data = [self yher_sendSynchronousRequest:request returningResponse:response error:error];
|
|
|
|
|
|
@try {
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable && !error && ([[request.URL host] isEqualToString:YHEventReportHttpHost] || [hostInHTTPField isEqualToString:YHEventReportHttpHost])) {
|
|
|
NSURLResponse *res = *response;
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)res;
|
|
|
NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields];
|
|
|
NSString *contentLength = [NSString stringWithFormat:@"%lld",[[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue]];
|
|
|
NSString *statusCode = [NSString stringWithFormat:@"%ld",(long)httpResponse.statusCode];
|
|
|
NSString *HTTPBodyStr = [[NSString alloc]initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
|
|
|
[[YH_EventCollector sharedInstance] timeEventEndWithRequestUrl:[request.URL absoluteString] HTTPMethod:request.HTTPMethod statusCode:statusCode contentLength:contentLength HTTPBodyStr:HTTPBodyStr];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
+ (void)yher_sendAsynchronousRequest:(NSURLRequest*) request
|
|
|
queue:(NSOperationQueue*) queue
|
|
|
completionHandler:(void (^)(NSURLResponse* _Nullable response, NSData* _Nullable data, NSError* _Nullable connectionError)) handler
|
|
|
{
|
|
|
NSString *hostInHTTPField = [request.allHTTPHeaderFields objectForKey:@"Host"];
|
|
|
|
|
|
@try {
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable && ([[request.URL host] isEqualToString:YHEventReportHttpHost] || [hostInHTTPField isEqualToString:YHEventReportHttpHost])) {
|
|
|
[[YH_EventCollector sharedInstance] timeEventStartWithRequestUrl:[request.URL absoluteString]];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
|
|
|
[self yher_sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
|
|
|
|
|
|
if (handler) {
|
|
|
handler(response,data,connectionError);
|
|
|
}
|
|
|
|
|
|
@try {
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable && !connectionError && ([[request.URL host] isEqualToString:YHEventReportHttpHost] || [hostInHTTPField isEqualToString:YHEventReportHttpHost])) {
|
|
|
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
|
|
|
NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields];
|
|
|
NSString *contentLength = [NSString stringWithFormat:@"%lld",[[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue]];
|
|
|
NSString *statusCode = [NSString stringWithFormat:@"%ld",(long)httpResponse.statusCode];
|
|
|
NSString *HTTPBodyStr = [[NSString alloc]initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
|
|
|
[[YH_EventCollector sharedInstance] timeEventEndWithRequestUrl:[request.URL absoluteString] HTTPMethod:request.HTTPMethod statusCode:statusCode contentLength:contentLength HTTPBodyStr:HTTPBodyStr];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
}];
|
|
|
}
|
|
|
|
|
|
#pragma mark - NSURLConnectionDelegate
|
|
|
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
|
|
connection.my_response = response;
|
|
|
|
|
|
if (self.my_delegate && [self.my_delegate respondsToSelector:@selector(connection:didReceiveResponse:)]) {
|
|
|
[self.my_delegate connection:connection didReceiveResponse:response];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
|
|
if (self.my_delegate && [self.my_delegate respondsToSelector:@selector(connection:didReceiveData:)]) {
|
|
|
[self.my_delegate connection:connection didReceiveData:data];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
|
|
|
if (self.my_delegate && [self.my_delegate respondsToSelector:@selector(connectionDidFinishLoading:)]) {
|
|
|
[self.my_delegate connectionDidFinishLoading:connection];
|
|
|
}
|
|
|
|
|
|
@try {
|
|
|
NSString *hostInHTTPField = [connection.currentRequest.allHTTPHeaderFields objectForKey:@"Host"];
|
|
|
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isHttpPerformanceTrackEnable && ([[connection.currentRequest.URL host] isEqualToString:YHEventReportHttpHost] || [hostInHTTPField isEqualToString:YHEventReportHttpHost])) {
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)connection.my_response;
|
|
|
NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields];
|
|
|
NSString *contentLength = [NSString stringWithFormat:@"%lld",[[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue]];
|
|
|
NSString *statusCode = [NSString stringWithFormat:@"%ld",(long)httpResponse.statusCode];
|
|
|
NSString *HTTPBodyStr = [[NSString alloc]initWithData:connection.currentRequest.HTTPBody encoding:NSUTF8StringEncoding];
|
|
|
[[YH_EventCollector sharedInstance] timeEventEndWithRequestUrl:[connection.currentRequest.URL absoluteString] HTTPMethod:connection.currentRequest.HTTPMethod statusCode:statusCode contentLength:contentLength HTTPBodyStr:HTTPBodyStr];
|
|
|
}
|
|
|
} @catch (NSException *exception) {
|
|
|
YHLog(@"%@ error: %@", self, exception);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
|
|
if (self.my_delegate && [self.my_delegate respondsToSelector:@selector(connection:didFailWithError:)]) {
|
|
|
[self.my_delegate connection:connection didFailWithError:error];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
@end |