更换腾讯cos上传
This commit is contained in:
18
Pods/QCloudCore/QCloudCore/Classes/Base/Bolts-Promise/QCloudThreadSafeMutableDictionary.h
generated
Normal file
18
Pods/QCloudCore/QCloudCore/Classes/Base/Bolts-Promise/QCloudThreadSafeMutableDictionary.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// QCloudThreadSafeMutableDictionary.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/31.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudThreadSafeMutableDictionary : NSObject
|
||||
- (id)objectForKey:(id)aKey;
|
||||
- (void)removeObjectForKey:(id)aKey;
|
||||
- (void)removeObject:(id)object;
|
||||
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey;
|
||||
- (NSArray *)allKeys;
|
||||
- (NSArray *)allValues;
|
||||
@end
|
||||
78
Pods/QCloudCore/QCloudCore/Classes/Base/Bolts-Promise/QCloudThreadSafeMutableDictionary.m
generated
Normal file
78
Pods/QCloudCore/QCloudCore/Classes/Base/Bolts-Promise/QCloudThreadSafeMutableDictionary.m
generated
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// QCloudThreadSafeMutableDictionary.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/31.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudThreadSafeMutableDictionary.h"
|
||||
|
||||
@interface QCloudThreadSafeMutableDictionary ()
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *dictionary;
|
||||
@property (nonatomic, strong) dispatch_queue_t dispatchQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudThreadSafeMutableDictionary
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_dictionary = [NSMutableDictionary new];
|
||||
_dispatchQueue = dispatch_queue_create("com.tencent.qcloud.safedicationary", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)aKey {
|
||||
__block id returnObject = nil;
|
||||
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
returnObject = [self.dictionary objectForKey:aKey];
|
||||
});
|
||||
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(id)aKey {
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
[self.dictionary removeObjectForKey:aKey];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
[self.dictionary setObject:anObject forKey:aKey];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray *)allKeys {
|
||||
__block NSArray *allKeys = nil;
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
allKeys = [self.dictionary allKeys];
|
||||
});
|
||||
return allKeys;
|
||||
}
|
||||
|
||||
- (NSArray *)allValues {
|
||||
__block NSArray *allValues = nil;
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
allValues = [self.dictionary allValues];
|
||||
});
|
||||
return allValues;
|
||||
}
|
||||
|
||||
- (void)removeObject:(id)object {
|
||||
dispatch_sync(self.dispatchQueue, ^{
|
||||
for (NSString *key in self.dictionary) {
|
||||
if (object == self.dictionary[key]) {
|
||||
[self.dictionary removeObjectForKey:key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
23
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoader.h
generated
Normal file
23
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoader.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// QCloudCustomLoader.h
|
||||
// Pods
|
||||
//
|
||||
// Created by garenwang on 2024/12/30.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudCustomSession.h"
|
||||
#import "QCloudCustomLoaderTask.h"
|
||||
#import "QCloudHTTPRequest.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol QCloudCustomLoader <NSObject>
|
||||
|
||||
-(QCloudCustomSession *)session;
|
||||
|
||||
-(BOOL)enable:(QCloudHTTPRequest *)httpRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
35
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoaderTask.h
generated
Normal file
35
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoaderTask.h
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// QCloudCustomLoaderTask.h
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/26.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@class QCloudCustomSession;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudCustomLoaderTask : NSURLSessionDataTask
|
||||
|
||||
@property (nullable, readwrite, copy) NSHTTPURLResponse *response;
|
||||
@property (nullable, readwrite, copy) NSURLRequest *currentRequest;
|
||||
@property (nullable, readwrite, copy) NSURLRequest *originalRequest;
|
||||
@property (atomic, assign) int64_t countOfBytesSent;
|
||||
@property (atomic, assign) int64_t countOfBytesExpectedToSend;
|
||||
/// 子类实现,用于构建自定义task。
|
||||
/// - Parameters:
|
||||
/// - httpRequest: SDK 构建好的 NSMutableURLRequest示例对象。
|
||||
/// - fromFile: 上传文件的本地路径,只有上传文件格式为文件路径时才有值。
|
||||
/// - session: 自定义session ,QCloudCustomSession的子类实例。
|
||||
- (instancetype)initWithHTTPRequest:(NSMutableURLRequest *)httpRequest
|
||||
fromFile:(NSURL *)fromFile
|
||||
session:(QCloudCustomSession *)session;
|
||||
|
||||
/// 子类实现,用于开启任务。
|
||||
-(void)resume;
|
||||
|
||||
/// 子类实现,用于取消当前任务。
|
||||
- (void)cancel;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
37
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoaderTask.m
generated
Normal file
37
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomLoaderTask.m
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// QCloudCustomLoaderTask.m
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/26.
|
||||
//
|
||||
|
||||
#import "QCloudCustomLoaderTask.h"
|
||||
#import "QCloudCustomSession.h"
|
||||
#import "QCloudError.h"
|
||||
|
||||
@interface QCloudCustomLoaderTask ()
|
||||
@property (nonatomic,strong)NSMutableURLRequest * httpRequest;
|
||||
@property (nonatomic,strong)QCloudCustomSession * session;
|
||||
@end
|
||||
|
||||
@implementation QCloudCustomLoaderTask
|
||||
@synthesize response = _response;
|
||||
@synthesize originalRequest = _originalRequest;
|
||||
@synthesize currentRequest = _currentRequest;
|
||||
@synthesize countOfBytesSent = _countOfBytesSent;
|
||||
@synthesize countOfBytesExpectedToSend = _countOfBytesExpectedToSend;
|
||||
|
||||
|
||||
- (instancetype)initWithHTTPRequest:(NSMutableURLRequest *)httpRequest
|
||||
fromFile:(NSURL *)fromFile
|
||||
session:(QCloudCustomSession *)session{
|
||||
@throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现" userInfo:nil];
|
||||
}
|
||||
|
||||
-(void)resume{
|
||||
@throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现" userInfo:nil];
|
||||
}
|
||||
- (void)cancel{
|
||||
@throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现" userInfo:nil];
|
||||
}
|
||||
@end
|
||||
91
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomSession.h
generated
Normal file
91
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomSession.h
generated
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// QCloudCustomSession.h
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/26.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class QCloudCustomLoaderTask;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@interface QCloudCustomSession : NSURLSession
|
||||
@property (nonatomic,
|
||||
weak)id<NSURLSessionTaskDelegate, NSURLSessionDataDelegate>customDelegate;
|
||||
|
||||
/// 需要子类实现,由COS SDK 进行回调。
|
||||
/// - Parameters:
|
||||
/// - request: SDK 传出来的请求实例。
|
||||
/// - fromFile: 以文件路径进行上传时的本地文件路径。
|
||||
-(QCloudCustomLoaderTask *)taskWithRequset:(NSMutableURLRequest *)request
|
||||
fromFile:(NSURL *)fromFile;
|
||||
|
||||
|
||||
/// 以下方法无需子类实现。供业务层调用,用于将自定义网络相应数据传给COS SDK。
|
||||
/******************************************************************************/
|
||||
|
||||
/// 处理数据任务接收到响应时的情况
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - response:请求响应数据
|
||||
/// - completionHandler: 完成回调
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveResponse:(NSURLResponse *)response
|
||||
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler;
|
||||
|
||||
|
||||
/// 监控上传任务的进度
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - bytesSent: 当前发送数据
|
||||
/// - totalBytesSent: 总共发送数据
|
||||
/// - totalBytesExpectedToSend: 总共待发送数据
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
|
||||
|
||||
|
||||
/// 接受到数据
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - data: 接受到的数据
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveData:(NSData *)data;
|
||||
|
||||
|
||||
/// 任务完成
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - error: 错误信息, SDK内会根据error 中的错误信息判断是否需要重试。
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didCompleteWithError:(NSError *)error;
|
||||
|
||||
|
||||
/// 处理身份验证
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - challenge: NSURLAuthenticationChallenge
|
||||
/// - completionHandler: 完成回调
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge
|
||||
completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *_Nullable credential))completionHandler;
|
||||
|
||||
|
||||
/// 收集性能参数
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - metrics: NSURLSessionTaskMetrics 请求性能参数
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(ios(10.0));
|
||||
|
||||
|
||||
/// 处理重定向
|
||||
/// - Parameters:
|
||||
/// - task: 自定义Task QCloudCustomLoaderTask子类
|
||||
/// - response: 请求响应
|
||||
/// - request: 重定向的请求
|
||||
/// - completionHandler: 完成回调
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
67
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomSession.m
generated
Normal file
67
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudCustomSession.m
generated
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// QCloudCustomSession.m
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/26.
|
||||
//
|
||||
|
||||
#import "QCloudCustomSession.h"
|
||||
#import "QCloudCustomLoaderTask.h"
|
||||
#import "QCloudError.h"
|
||||
@implementation QCloudCustomSession
|
||||
|
||||
-(QCloudCustomLoaderTask *)taskWithRequset:(NSMutableURLRequest *)request
|
||||
fromFile:(NSURL *)fromFile{
|
||||
@throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现" userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveResponse:(NSURLResponse *)response
|
||||
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
|
||||
task.response = (NSHTTPURLResponse *)response;
|
||||
[self.customDelegate URLSession:self dataTask:task didReceiveResponse:response completionHandler:completionHandler];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)]) {
|
||||
[self.customDelegate URLSession:self task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveData:(NSData *)data{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:dataTask:didReceiveData:)]) {
|
||||
[self.customDelegate URLSession:self dataTask:task didReceiveData:data];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didCompleteWithError:(NSError *)error{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
|
||||
[self.customDelegate URLSession:self task:task didCompleteWithError:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge
|
||||
completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *_Nullable credential))completionHandler{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) {
|
||||
[self.customDelegate URLSession:self task:task didReceiveChallenge:challenge completionHandler:completionHandler];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(ios(10.0)){
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:task:didFinishCollectingMetrics:)]) {
|
||||
[self.customDelegate URLSession:self task:task didFinishCollectingMetrics:metrics];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)customTask:(QCloudCustomLoaderTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{
|
||||
if ([self.customDelegate respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) {
|
||||
task.currentRequest = request;
|
||||
[self.customDelegate URLSession:self task:task willPerformHTTPRedirection:response newRequest:request completionHandler:completionHandler];
|
||||
}
|
||||
}
|
||||
@end
|
||||
27
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudLoaderManager.h
generated
Normal file
27
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudLoaderManager.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// QCloudLoaderManager.h
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/27.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudCustomSession.h"
|
||||
#import "QCloudCustomLoader.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@interface QCloudLoaderManager :NSObject
|
||||
|
||||
@property (nonatomic,assign)BOOL enable;
|
||||
|
||||
@property (atomic,strong,readonly)NSMutableArray <id <QCloudCustomLoader>> * loaders;
|
||||
|
||||
- (void)addLoader:(id <QCloudCustomLoader>)loader;
|
||||
|
||||
+ (QCloudLoaderManager *)manager;
|
||||
|
||||
-(id <QCloudCustomLoader>)getAvailableLoader:(QCloudHTTPRequest *)httpRequest;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
52
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudLoaderManager.m
generated
Normal file
52
Pods/QCloudCore/QCloudCore/Classes/Base/CustomLoader/QCloudLoaderManager.m
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// QCloudLoaderManager.m
|
||||
// Pods-QCloudCOSXMLDemo
|
||||
//
|
||||
// Created by garenwang on 2024/12/27.
|
||||
//
|
||||
|
||||
#import "QCloudLoaderManager.h"
|
||||
#import "QCloudHTTPRequest.h"
|
||||
#import "QCloudHTTPSessionManager.h"
|
||||
@interface QCloudLoaderManager()
|
||||
@property (atomic,strong)NSMutableArray <id <QCloudCustomLoader>> * loaders;
|
||||
@end
|
||||
|
||||
@implementation QCloudLoaderManager
|
||||
|
||||
+ (QCloudLoaderManager *)manager {
|
||||
static QCloudLoaderManager *manager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[QCloudLoaderManager alloc] init];
|
||||
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.loaders = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id <QCloudCustomLoader>)getAvailableLoader:(QCloudHTTPRequest *)httpRequest{
|
||||
for (int i = 0; i < self.loaders.count; i ++) {
|
||||
if ([self.loaders[i] enable:httpRequest]) {
|
||||
return self.loaders[i];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(void)addLoader:(id <QCloudCustomLoader>)loader{
|
||||
@synchronized (self) {
|
||||
loader.session.customDelegate = [QCloudHTTPSessionManager shareClient];
|
||||
[self.loaders addObject:loader];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
29
Pods/QCloudCore/QCloudCore/Classes/Base/DateTools/NSDate+QCloudComapre.h
generated
Normal file
29
Pods/QCloudCore/QCloudCore/Classes/Base/DateTools/NSDate+QCloudComapre.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// NSDate+QCloudComapre.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/4/5.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSDate (QCloudComapre)
|
||||
- (BOOL)qcloud_isEarlierThan:(NSDate *)date;
|
||||
/**
|
||||
* Returns a YES if receiver is later than provided comparison date, otherwise returns NO
|
||||
*
|
||||
* @param date NSDate - Provided date for comparison
|
||||
*
|
||||
* @return BOOL representing comparison result
|
||||
*/
|
||||
- (BOOL)qcloud_isLaterThan:(NSDate *)date;
|
||||
/**
|
||||
* Returns a YES if receiver is earlier than or equal to the provided comparison date, otherwise returns NO
|
||||
*
|
||||
* @param date NSDate - Provided date for comparison
|
||||
*
|
||||
* @return BOOL representing comparison result
|
||||
*/
|
||||
- (BOOL)qcloud_isEarlierThanOrEqualTo:(NSDate *)date;
|
||||
@end
|
||||
55
Pods/QCloudCore/QCloudCore/Classes/Base/DateTools/NSDate+QCloudComapre.m
generated
Normal file
55
Pods/QCloudCore/QCloudCore/Classes/Base/DateTools/NSDate+QCloudComapre.m
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// NSDate+QCloudComapre.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/4/5.
|
||||
//
|
||||
//
|
||||
|
||||
#import "NSDate+QCloudComapre.h"
|
||||
|
||||
@implementation NSDate (QCloudComapre)
|
||||
#pragma mark Comparators
|
||||
/**
|
||||
* Returns a YES if receiver is earlier than provided comparison date, otherwise returns NO
|
||||
*
|
||||
* @param date NSDate - Provided date for comparison
|
||||
*
|
||||
* @return BOOL representing comparison result
|
||||
*/
|
||||
- (BOOL)qcloud_isEarlierThan:(NSDate *)date {
|
||||
if (self.timeIntervalSince1970 < date.timeIntervalSince1970) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a YES if receiver is later than provided comparison date, otherwise returns NO
|
||||
*
|
||||
* @param date NSDate - Provided date for comparison
|
||||
*
|
||||
* @return BOOL representing comparison result
|
||||
*/
|
||||
- (BOOL)qcloud_isLaterThan:(NSDate *)date {
|
||||
if (self.timeIntervalSince1970 > date.timeIntervalSince1970) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a YES if receiver is earlier than or equal to the provided comparison date, otherwise returns NO
|
||||
*
|
||||
* @param date NSDate - Provided date for comparison
|
||||
*
|
||||
* @return BOOL representing comparison result
|
||||
*/
|
||||
- (BOOL)qcloud_isEarlierThanOrEqualTo:(NSDate *)date {
|
||||
if (self.timeIntervalSince1970 <= date.timeIntervalSince1970) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
57
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/QCloudFCUUID.h
generated
Normal file
57
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/QCloudFCUUID.h
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// QCloudFCUUID.h
|
||||
//
|
||||
// Created by Fabio Caccamo on 26/06/14.
|
||||
// Copyright © 2016 Fabio Caccamo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString *const QCloudFCUUIDsOfUserDevicesDidChangeNotification;
|
||||
|
||||
@interface QCloudFCUUID : NSObject {
|
||||
NSMutableDictionary *_uuidForKey;
|
||||
NSString *_uuidForSession;
|
||||
NSString *_uuidForInstallation;
|
||||
NSString *_uuidForVendor;
|
||||
NSString *_uuidForDevice;
|
||||
NSString *_uuidsOfUserDevices;
|
||||
BOOL _uuidsOfUserDevices_iCloudAvailable;
|
||||
}
|
||||
/**
|
||||
每次运行应用都会变
|
||||
*/
|
||||
+ (NSString *)uuid;
|
||||
/**
|
||||
changes each time (no persistent), but allows to keep in memory more temporary uuids
|
||||
*/
|
||||
+ (NSString *)uuidForKey:(id<NSCopying>)key;
|
||||
/**
|
||||
每次运行应用都会变
|
||||
*/
|
||||
+ (NSString *)uuidForSession;
|
||||
/**
|
||||
重新安装的时候会变
|
||||
*/
|
||||
+ (NSString *)uuidForInstallation;
|
||||
/**
|
||||
卸载后重装会变
|
||||
*/
|
||||
+ (NSString *)uuidForVendor;
|
||||
/**
|
||||
抹掉iPhone的时候才会变,适合做唯一标识
|
||||
*/
|
||||
+ (NSString *)uuidForDevice;
|
||||
+ (NSString *)uuidForDeviceMigratingValue:(NSString *)value commitMigration:(BOOL)commitMigration;
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key commitMigration:(BOOL)commitMigration;
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key service:(NSString *)service commitMigration:(BOOL)commitMigration;
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup
|
||||
commitMigration:(BOOL)commitMigration;
|
||||
+ (NSArray *)uuidsOfUserDevices;
|
||||
+ (NSArray *)uuidsOfUserDevicesExcludingCurrentDevice;
|
||||
|
||||
+ (BOOL)uuidValueIsValid:(NSString *)uuidValue;
|
||||
|
||||
@end
|
||||
427
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/QCloudFCUUID.m
generated
Normal file
427
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/QCloudFCUUID.m
generated
Normal file
@@ -0,0 +1,427 @@
|
||||
//
|
||||
// QCloudFCUUID.m
|
||||
//
|
||||
// Created by Fabio Caccamo on 26/06/14.
|
||||
// Copyright © 2016 Fabio Caccamo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudFCUUID.h"
|
||||
#import "QCloudUICKeyChainStore.h"
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
@implementation QCloudFCUUID
|
||||
|
||||
NSString *const QCloudFCUUIDsOfUserDevicesDidChangeNotification = @"QCloudFCUUIDsOfUserDevicesDidChangeNotification";
|
||||
|
||||
static NSString *const _uuidForInstallationKey = @"fc_uuidForInstallation";
|
||||
static NSString *const _uuidForDeviceKey = @"fc_uuidForDevice";
|
||||
static NSString *const _uuidsOfUserDevicesKey = @"fc_uuidsOfUserDevices";
|
||||
static NSString *const _uuidsOfUserDevicesToggleKey = @"fc_uuidsOfUserDevicesToggle";
|
||||
|
||||
+ (QCloudFCUUID *)sharedInstance {
|
||||
static QCloudFCUUID *instance = nil;
|
||||
static dispatch_once_t token;
|
||||
|
||||
dispatch_once(&token, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
[self uuidsOfUserDevices_iCloudInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)_getOrCreateValueForKey:(NSString *)key
|
||||
defaultValue:(NSString *)defaultValue
|
||||
userDefaults:(BOOL)userDefaults
|
||||
keychain:(BOOL)keychain
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup
|
||||
synchronizable:(BOOL)synchronizable {
|
||||
NSString *value = [self _getValueForKey:key userDefaults:userDefaults keychain:keychain service:service accessGroup:accessGroup];
|
||||
|
||||
if (!value) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
value = [self uuid];
|
||||
}
|
||||
|
||||
[self _setValue:value
|
||||
forKey:key
|
||||
userDefaults:userDefaults
|
||||
keychain:keychain
|
||||
service:service
|
||||
accessGroup:accessGroup
|
||||
synchronizable:synchronizable];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (NSString *)_getValueForKey:(NSString *)key
|
||||
userDefaults:(BOOL)userDefaults
|
||||
keychain:(BOOL)keychain
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup {
|
||||
NSString *value = nil;
|
||||
|
||||
if (!value && keychain) {
|
||||
value = [QCloudUICKeyChainStore stringForKey:key service:service accessGroup:accessGroup];
|
||||
}
|
||||
|
||||
if (!value && userDefaults) {
|
||||
value = [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)_setValue:(NSString *)value
|
||||
forKey:(NSString *)key
|
||||
userDefaults:(BOOL)userDefaults
|
||||
keychain:(BOOL)keychain
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup
|
||||
synchronizable:(BOOL)synchronizable {
|
||||
if (value && userDefaults) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
if (value && keychain) {
|
||||
QCloudUICKeyChainStore *keychain = [QCloudUICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
|
||||
[keychain setSynchronizable:synchronizable];
|
||||
[keychain setString:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)uuid {
|
||||
// also known as qcloud_uuid/universallyUniqueIdentifier
|
||||
|
||||
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
|
||||
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
|
||||
CFRelease(uuidRef);
|
||||
|
||||
NSString *uuidValue = (__bridge_transfer NSString *)uuidStringRef;
|
||||
uuidValue = [uuidValue lowercaseString];
|
||||
uuidValue = [uuidValue stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
||||
return uuidValue;
|
||||
}
|
||||
|
||||
- (NSString *)uuidForKey:(id<NSCopying>)key {
|
||||
if (_uuidForKey == nil) {
|
||||
_uuidForKey = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
NSString *uuidValue = [_uuidForKey objectForKey:key];
|
||||
|
||||
if (uuidValue == nil) {
|
||||
uuidValue = [self uuid];
|
||||
|
||||
[_uuidForKey setObject:uuidValue forKey:key];
|
||||
}
|
||||
|
||||
return uuidValue;
|
||||
}
|
||||
|
||||
- (NSString *)uuidForSession {
|
||||
if (_uuidForSession == nil) {
|
||||
_uuidForSession = [self uuid];
|
||||
}
|
||||
|
||||
return _uuidForSession;
|
||||
}
|
||||
|
||||
- (NSString *)uuidForInstallation {
|
||||
if (_uuidForInstallation == nil) {
|
||||
_uuidForInstallation = [self _getOrCreateValueForKey:_uuidForInstallationKey
|
||||
defaultValue:nil
|
||||
userDefaults:YES
|
||||
keychain:NO
|
||||
service:nil
|
||||
accessGroup:nil
|
||||
synchronizable:NO];
|
||||
}
|
||||
|
||||
return _uuidForInstallation;
|
||||
}
|
||||
|
||||
- (NSString *)uuidForVendor {
|
||||
if (_uuidForVendor == nil) {
|
||||
#if TARGET_OS_IOS
|
||||
_uuidForVendor = [[[[[UIDevice currentDevice] identifierForVendor] UUIDString] lowercaseString] stringByReplacingOccurrencesOfString:@"-"
|
||||
withString:@""];
|
||||
|
||||
#elif TARGET_OS_MAC
|
||||
_uuidForVendor = @"0000";
|
||||
#endif
|
||||
}
|
||||
|
||||
return _uuidForVendor;
|
||||
}
|
||||
|
||||
- (void)uuidForDevice_updateWithValue:(NSString *)value {
|
||||
_uuidForDevice = [NSString stringWithString:value];
|
||||
[self _setValue:_uuidForDevice forKey:_uuidForDeviceKey userDefaults:YES keychain:YES service:nil accessGroup:nil synchronizable:NO];
|
||||
}
|
||||
|
||||
- (NSString *)uuidForDevice {
|
||||
// also known as udid/uniqueDeviceIdentifier but this doesn't persists to system reset
|
||||
|
||||
if (_uuidForDevice == nil) {
|
||||
_uuidForDevice = [self _getOrCreateValueForKey:_uuidForDeviceKey
|
||||
defaultValue:nil
|
||||
userDefaults:YES
|
||||
keychain:YES
|
||||
service:nil
|
||||
accessGroup:nil
|
||||
synchronizable:NO];
|
||||
}
|
||||
|
||||
return _uuidForDevice;
|
||||
}
|
||||
|
||||
- (NSString *)uuidForDeviceMigratingValue:(NSString *)value commitMigration:(BOOL)commitMigration {
|
||||
if ([self uuidValueIsValid:value]) {
|
||||
NSString *oldValue = [self uuidForDevice];
|
||||
NSString *newValue = [NSString stringWithString:value];
|
||||
|
||||
if ([oldValue isEqualToString:newValue]) {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
if (commitMigration) {
|
||||
[self uuidForDevice_updateWithValue:newValue];
|
||||
|
||||
NSMutableOrderedSet *uuidsOfUserDevicesSet = [[NSMutableOrderedSet alloc] initWithArray:[self uuidsOfUserDevices]];
|
||||
[uuidsOfUserDevicesSet addObject:newValue];
|
||||
[uuidsOfUserDevicesSet removeObject:oldValue];
|
||||
|
||||
[self uuidsOfUserDevices_updateWithValue:[uuidsOfUserDevicesSet array]];
|
||||
[self uuidsOfUserDevices_iCloudSync];
|
||||
|
||||
return [self uuidForDevice];
|
||||
} else {
|
||||
return oldValue;
|
||||
}
|
||||
} else {
|
||||
[NSException raise:@"Invalid qcloud_uuid to migrate" format:@"qcloud_uuid value should be a string of 32 or 36 characters."];
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key commitMigration:(BOOL)commitMigration {
|
||||
return [self uuidForDeviceMigratingValueForKey:key service:nil accessGroup:nil commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
- (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key service:(NSString *)service commitMigration:(BOOL)commitMigration {
|
||||
return [self uuidForDeviceMigratingValueForKey:key service:service accessGroup:nil commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
- (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup
|
||||
commitMigration:(BOOL)commitMigration {
|
||||
NSString *uuidToMigrate = [self _getValueForKey:key userDefaults:YES keychain:YES service:service accessGroup:accessGroup];
|
||||
|
||||
return [self uuidForDeviceMigratingValue:uuidToMigrate commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
- (void)uuidsOfUserDevices_iCloudInit {
|
||||
_uuidsOfUserDevices_iCloudAvailable = NO;
|
||||
|
||||
if (NSClassFromString(@"NSUbiquitousKeyValueStore")) {
|
||||
NSUbiquitousKeyValueStore *iCloud = [NSUbiquitousKeyValueStore defaultStore];
|
||||
|
||||
if (iCloud) {
|
||||
_uuidsOfUserDevices_iCloudAvailable = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(uuidsOfUserDevices_iCloudChange:)
|
||||
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
|
||||
object:nil];
|
||||
|
||||
[self uuidsOfUserDevices_iCloudSync];
|
||||
} else {
|
||||
// NSLog(@"iCloud not available");
|
||||
}
|
||||
} else {
|
||||
// NSLog(@"iOS < 5");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)uuidsOfUserDevices_iCloudSync {
|
||||
if (_uuidsOfUserDevices_iCloudAvailable) {
|
||||
NSUbiquitousKeyValueStore *iCloud = [NSUbiquitousKeyValueStore defaultStore];
|
||||
|
||||
// if keychain contains more device identifiers than icloud, maybe that icloud has been empty, so re-write these identifiers to iCloud
|
||||
for (NSString *uuidOfUserDevice in [self uuidsOfUserDevices]) {
|
||||
NSString *uuidOfUserDeviceAsKey = [NSString stringWithFormat:@"%@_%@", _uuidForDeviceKey, uuidOfUserDevice];
|
||||
|
||||
if (![[iCloud stringForKey:uuidOfUserDeviceAsKey] isEqualToString:uuidOfUserDevice]) {
|
||||
[iCloud setString:uuidOfUserDevice forKey:uuidOfUserDeviceAsKey];
|
||||
}
|
||||
}
|
||||
|
||||
// toggle a boolean value to force notification on other devices, useful for debug
|
||||
[iCloud setBool:![iCloud boolForKey:_uuidsOfUserDevicesToggleKey] forKey:_uuidsOfUserDevicesToggleKey];
|
||||
[iCloud synchronize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)uuidsOfUserDevices_iCloudChange:(NSNotification *)notification {
|
||||
if (_uuidsOfUserDevices_iCloudAvailable) {
|
||||
NSMutableOrderedSet *uuidsSet = [[NSMutableOrderedSet alloc] initWithArray:[self uuidsOfUserDevices]];
|
||||
NSInteger uuidsCount = [uuidsSet count];
|
||||
|
||||
NSUbiquitousKeyValueStore *iCloud = [NSUbiquitousKeyValueStore defaultStore];
|
||||
NSDictionary *iCloudDict = [iCloud dictionaryRepresentation];
|
||||
|
||||
// NSLog(@"uuidsOfUserDevicesSync: %@", iCloudDict);
|
||||
|
||||
[iCloudDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
NSString *uuidKey = (NSString *)key;
|
||||
|
||||
if ([uuidKey rangeOfString:_uuidForDeviceKey].location == 0) {
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
NSString *uuidValue = (NSString *)obj;
|
||||
|
||||
if ([uuidKey rangeOfString:uuidValue].location != NSNotFound && [self uuidValueIsValid:uuidValue]) {
|
||||
// NSLog(@"qcloud_uuid: %@", uuidValue);
|
||||
|
||||
[uuidsSet addObject:uuidValue];
|
||||
} else {
|
||||
// NSLog(@"invalid qcloud_uuid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
if ([uuidsSet count] > uuidsCount) {
|
||||
[self uuidsOfUserDevices_updateWithValue:[uuidsSet array]];
|
||||
|
||||
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[self uuidsOfUserDevices] forKey:@"uuidsOfUserDevices"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:QCloudFCUUIDsOfUserDevicesDidChangeNotification object:self userInfo:userInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)uuidsOfUserDevices_updateWithValue:(NSArray *)value {
|
||||
_uuidsOfUserDevices = [value componentsJoinedByString:@"|"];
|
||||
[self _setValue:_uuidsOfUserDevices forKey:_uuidsOfUserDevicesKey userDefaults:YES keychain:YES service:nil accessGroup:nil synchronizable:YES];
|
||||
}
|
||||
|
||||
- (NSArray *)uuidsOfUserDevices {
|
||||
if (_uuidsOfUserDevices == nil) {
|
||||
_uuidsOfUserDevices = [self _getOrCreateValueForKey:_uuidsOfUserDevicesKey
|
||||
defaultValue:[self uuidForDevice]
|
||||
userDefaults:YES
|
||||
keychain:YES
|
||||
service:nil
|
||||
accessGroup:nil
|
||||
synchronizable:YES];
|
||||
}
|
||||
|
||||
return [_uuidsOfUserDevices componentsSeparatedByString:@"|"];
|
||||
}
|
||||
|
||||
- (NSArray *)uuidsOfUserDevicesExcludingCurrentDevice {
|
||||
NSMutableArray *uuids = [NSMutableArray arrayWithArray:[self uuidsOfUserDevices]];
|
||||
[uuids removeObject:[self uuidForDevice]];
|
||||
return [NSArray arrayWithArray:uuids];
|
||||
}
|
||||
|
||||
- (BOOL)uuidValueIsValid:(NSString *)uuidValue {
|
||||
if (uuidValue != nil) {
|
||||
NSString *uuidPattern = @"^[0-9a-f]{32}|[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$";
|
||||
NSRegularExpression *uuidRegExp = [NSRegularExpression regularExpressionWithPattern:uuidPattern
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:nil];
|
||||
|
||||
NSRange uuidValueRange = NSMakeRange(0, [uuidValue length]);
|
||||
NSRange uuidMatchRange = [uuidRegExp rangeOfFirstMatchInString:uuidValue options:0 range:uuidValueRange];
|
||||
NSString *uuidMatchValue;
|
||||
|
||||
if (!NSEqualRanges(uuidMatchRange, NSMakeRange(NSNotFound, 0))) {
|
||||
uuidMatchValue = [uuidValue substringWithRange:uuidMatchRange];
|
||||
|
||||
if ([uuidMatchValue isEqualToString:uuidValue]) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)uuid {
|
||||
return [[self sharedInstance] uuid];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForKey:(id<NSCopying>)key {
|
||||
return [[self sharedInstance] uuidForKey:key];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForSession {
|
||||
return [[self sharedInstance] uuidForSession];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForInstallation {
|
||||
return [[self sharedInstance] uuidForInstallation];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForVendor {
|
||||
return [[self sharedInstance] uuidForVendor];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForDevice {
|
||||
return [[self sharedInstance] uuidForDevice];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForDeviceMigratingValue:(NSString *)value commitMigration:(BOOL)commitMigration {
|
||||
return [[self sharedInstance] uuidForDeviceMigratingValue:value commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key commitMigration:(BOOL)commitMigration {
|
||||
return [[self sharedInstance] uuidForDeviceMigratingValueForKey:key service:nil accessGroup:nil commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key service:(NSString *)service commitMigration:(BOOL)commitMigration {
|
||||
return [[self sharedInstance] uuidForDeviceMigratingValueForKey:key service:service accessGroup:nil commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
+ (NSString *)uuidForDeviceMigratingValueForKey:(NSString *)key
|
||||
service:(NSString *)service
|
||||
accessGroup:(NSString *)accessGroup
|
||||
commitMigration:(BOOL)commitMigration {
|
||||
return [[self sharedInstance] uuidForDeviceMigratingValueForKey:key service:service accessGroup:accessGroup commitMigration:commitMigration];
|
||||
}
|
||||
|
||||
+ (NSArray *)uuidsOfUserDevices {
|
||||
return [[self sharedInstance] uuidsOfUserDevices];
|
||||
}
|
||||
|
||||
+ (NSArray *)uuidsOfUserDevicesExcludingCurrentDevice {
|
||||
return [[self sharedInstance] uuidsOfUserDevicesExcludingCurrentDevice];
|
||||
}
|
||||
|
||||
+ (BOOL)uuidValueIsValid:(NSString *)uuidValue {
|
||||
return [[self sharedInstance] uuidValueIsValid:uuidValue];
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/UIDevice+QCloudFCUUID.h
generated
Normal file
17
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/UIDevice+QCloudFCUUID.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// UIDevice+QCloudFCUUID.h
|
||||
//
|
||||
// Created by Fabio Caccamo on 19/11/15.
|
||||
// Copyright © 2015 Fabio Caccamo. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "QCloudFCUUID.h"
|
||||
|
||||
@interface UIDevice (QCloudFCUUID)
|
||||
|
||||
- (NSString *)qcloud_uuid;
|
||||
|
||||
@end
|
||||
#endif
|
||||
17
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/UIDevice+QCloudFCUUID.m
generated
Normal file
17
Pods/QCloudCore/QCloudCore/Classes/Base/FCUUID/UIDevice+QCloudFCUUID.m
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// UIDevice+QCloudFCUUID.m
|
||||
//
|
||||
// Created by Fabio Caccamo on 19/11/15.
|
||||
// Copyright © 2015 Fabio Caccamo. All rights reserved.
|
||||
//
|
||||
#if TARGET_OS_IOS
|
||||
#import "UIDevice+QCloudFCUUID.h"
|
||||
|
||||
@implementation UIDevice (QCloudFCUUID)
|
||||
|
||||
- (NSString *)qcloud_uuid {
|
||||
return [QCloudFCUUID uuidForDevice];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
19
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/Encryt/QCloudEncryt.h
generated
Normal file
19
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/Encryt/QCloudEncryt.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// QCloudEncryt.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/6/6.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytNSDataMD5Base64(NSData *data);
|
||||
FOUNDATION_EXPORT NSString *QCloudEncrytNSDataMD5(NSData *data);
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytFileMD5Base64(NSString *filePath);
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytFileMD5(NSString *filePath);
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytFileOffsetMD5Base64(NSString *filePath, int64_t offset, int64_t siliceLength);
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytFileOffsetMD5(NSString *filePath, int64_t offset, int64_t siliceLength);
|
||||
FOUNDATION_EXTERN NSString *QCloudEncrytMD5String(NSString *originString);
|
||||
FOUNDATION_EXTERN NSString *QCloudHmacSha1Encrypt(NSString *data, NSString *key);
|
||||
FOUNDATION_EXTERN NSData *QCloudHmacEncrypt(NSString *data, NSString *key);
|
||||
216
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/Encryt/QCloudEncryt.mm
generated
Normal file
216
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/Encryt/QCloudEncryt.mm
generated
Normal file
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// QCloudEncryt.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/6/6.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudEncryt.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import "QCloudFileUtils.h"
|
||||
#import <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
@interface NSData(MD5Related)
|
||||
- (NSString*)qcloud_MD5String;
|
||||
@end
|
||||
|
||||
@implementation NSData(MD5Related)
|
||||
- (NSString*) qcloud_MD5String {
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
const unsigned char* buf = (const unsigned char*)self.bytes;
|
||||
NSMutableString* mutableString = [[NSMutableString alloc] init];
|
||||
for (int i = 0; i < self.length; i++) {
|
||||
[mutableString appendFormat:@"%02lX",(NSUInteger)buf[i]];
|
||||
}
|
||||
return [mutableString copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NSString* QCloudEncrytNSDataMD5Base64(NSData* data)
|
||||
{
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5( data.bytes, (CC_LONG)data.length, result ); // This is the md5 call
|
||||
NSData* md5data = [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
|
||||
return [md5data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
NSData* _internalEncrytFileMD5(NSString* filePath) {
|
||||
if (!QCloudFileExist(filePath)) {
|
||||
return nil;
|
||||
}
|
||||
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath];
|
||||
if(!handle)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
CC_MD5_CTX md5;
|
||||
CC_MD5_Init(&md5);
|
||||
BOOL done = NO;
|
||||
static NSUInteger MD5_CHUNK = 1024*16;
|
||||
while (!done)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSData *fileData = [handle readDataOfLength:MD5_CHUNK];
|
||||
CC_MD5_Update(&md5, [fileData bytes], (CC_LONG)[fileData length]);
|
||||
if([fileData length] == 0)
|
||||
done = YES;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char digest[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5_Final(digest, &md5);
|
||||
[handle closeFile];
|
||||
NSData* md5data = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
|
||||
return md5data;
|
||||
}
|
||||
|
||||
NSString* QCloudEncrytFileMD5Base64(NSString* filePath) {
|
||||
NSData* md5Data = _internalEncrytFileMD5(filePath);
|
||||
if (!md5Data) {
|
||||
return nil;
|
||||
}
|
||||
return [md5Data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
NSData* _internalEncrytNSDataMD5(NSData* data) {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5( data.bytes, (CC_LONG)data.length, result ); // This is the md5 call
|
||||
NSData* md5data = [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
|
||||
return md5data;
|
||||
}
|
||||
|
||||
NSString* QCloudEncrytNSDataMD5(NSData* data) {
|
||||
NSData* md5data = _internalEncrytNSDataMD5(data);
|
||||
if (!md5data) {
|
||||
return nil;
|
||||
}
|
||||
return [md5data qcloud_MD5String];
|
||||
}
|
||||
|
||||
NSString* QCloudEncrytFileMD5(NSString* filePath) {
|
||||
NSData* md5data = _internalEncrytFileMD5(filePath);
|
||||
return [md5data qcloud_MD5String];
|
||||
}
|
||||
|
||||
|
||||
NSData* _internalEncrytFileOffsetMD5(NSString* filePath, int64_t offset , int64_t siliceLength) {
|
||||
if (!QCloudFileExist(filePath)) {
|
||||
return nil;
|
||||
}
|
||||
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath];
|
||||
if(!handle)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
if (QCloudFileSize(filePath) < offset+siliceLength) {
|
||||
return nil;
|
||||
}
|
||||
[handle seekToFileOffset:offset];
|
||||
CC_MD5_CTX md5;
|
||||
CC_MD5_Init(&md5);
|
||||
BOOL done = NO;
|
||||
|
||||
NSUInteger totalReadCount = 0;
|
||||
static NSUInteger MD5_CHUNK = 1024*16;
|
||||
while (!done)
|
||||
{
|
||||
if (totalReadCount >= siliceLength) {
|
||||
break;
|
||||
}
|
||||
NSUInteger willReadLength = MIN(MD5_CHUNK, siliceLength-totalReadCount);
|
||||
if (willReadLength == 0) {
|
||||
break;
|
||||
}
|
||||
NSData *fileData = [handle readDataOfLength:willReadLength];
|
||||
if (fileData.length == 0) {
|
||||
break;
|
||||
}
|
||||
totalReadCount += fileData.length;
|
||||
CC_MD5_Update(&md5, [fileData bytes], (CC_LONG)[fileData length]);
|
||||
}
|
||||
|
||||
unsigned char digest[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5_Final(digest, &md5);
|
||||
[handle closeFile];
|
||||
NSData* md5data = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
|
||||
return md5data;
|
||||
}
|
||||
|
||||
NSString* QCloudEncrytFileOffsetMD5Base64(NSString* filePath, int64_t offset , int64_t siliceLength) {
|
||||
NSData* md5Data = _internalEncrytFileOffsetMD5(filePath, offset, siliceLength);
|
||||
if (!md5Data) {
|
||||
return nil;
|
||||
}
|
||||
return [md5Data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
|
||||
NSString* QCloudEncrytFileOffsetMD5(NSString* filePath, int64_t offset , int64_t siliceLength) {
|
||||
NSData* md5Data = _internalEncrytFileOffsetMD5(filePath, offset, siliceLength);
|
||||
if (!md5Data) {
|
||||
return nil;
|
||||
}
|
||||
return [md5Data qcloud_MD5String];
|
||||
}
|
||||
|
||||
|
||||
NSString* QCloudEncrytMD5String(NSString* originString) {
|
||||
const char *cStr = [originString UTF8String];
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
|
||||
|
||||
return [NSString stringWithFormat:
|
||||
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
NSString* QCloudHmacSha1Encrypt(NSString *data , NSString* key)
|
||||
{
|
||||
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
|
||||
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
|
||||
|
||||
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
|
||||
NSData *HMACData = [[NSData alloc] initWithBytes:cHMAC
|
||||
length:sizeof(cHMAC)];
|
||||
NSString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
|
||||
for (int i = 0; i < HMACData.length; ++i)
|
||||
HMAC = [HMAC stringByAppendingFormat:@"%02lx", (unsigned long)cHMAC[i]];
|
||||
|
||||
return HMAC;
|
||||
}
|
||||
|
||||
NSData* QCloudHmacEncrypt(NSString *data ,NSString *key){
|
||||
|
||||
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
|
||||
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
|
||||
NSData *hmac = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
|
||||
return hmac;
|
||||
}
|
||||
22
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCLSLoggerOutput.h
generated
Normal file
22
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCLSLoggerOutput.h
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// QCloudCLSLoggerOutput.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by garenwang on 2025/4/7.
|
||||
//
|
||||
|
||||
#import <QCloudCore/QCloudCore.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudCLSLoggerOutput : QCloudLoggerOutput
|
||||
|
||||
@property (nonatomic,strong,readonly)id clsService;
|
||||
- (instancetype)initWithTopicId:(NSString *)topicId endpoint:(NSString *)endPoint;
|
||||
|
||||
- (void)setupPermanentCredentialsSecretId:(NSString *)secretId secretKey:(NSString *)secretKey;
|
||||
|
||||
- (void)setupCredentialsRefreshBlock:(QCloudCredential * _Nonnull (^)(void))refreshBlock;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
141
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCLSLoggerOutput.m
generated
Normal file
141
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCLSLoggerOutput.m
generated
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// QCloudCLSLoggerOutput.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by garenwang on 2025/4/7.
|
||||
//
|
||||
|
||||
#import "QCloudCLSLoggerOutput.h"
|
||||
#import "QCloudLogModel.h"
|
||||
#import "NSDate+QCloud.h"
|
||||
NSString * const QCloudTrackCosSdkLog = @"qcloud_track_cos_sdk_log";
|
||||
@interface QCloudCLSLoggerOutput ()
|
||||
@property (nonatomic,strong)id clsService;
|
||||
@property (nonatomic, strong) dispatch_queue_t buildQueue;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation QCloudCLSLoggerOutput
|
||||
- (instancetype)initWithTopicId:(NSString *)topicId endpoint:(NSString *)endPoint {
|
||||
if (self = [super init]) {
|
||||
Class trackServiceClass = NSClassFromString(@"QCloudCLSTrackService");
|
||||
if (trackServiceClass) {
|
||||
SEL initSelector = NSSelectorFromString(@"initWithTopicId:endpoint:");
|
||||
if ([trackServiceClass instancesRespondToSelector:initSelector]) {
|
||||
_clsService = [[trackServiceClass alloc] performSelector:initSelector withObject:topicId withObject:endPoint];
|
||||
}
|
||||
}
|
||||
_buildQueue = dispatch_queue_create("com.tencent.qcloud.logger.cls.build", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)setupPermanentCredentialsSecretId:(NSString *)secretId secretKey:(NSString *)secretKey { // id 参数
|
||||
Class cla = NSClassFromString(@"QCloudClsSessionCredentials");
|
||||
if (!cla) {
|
||||
return;
|
||||
}
|
||||
id credentials = [[cla alloc]init];
|
||||
// 或更安全的版本(检查属性是否存在)
|
||||
SEL secretIdSelector = NSSelectorFromString(@"setSecretId:");
|
||||
if ([credentials respondsToSelector:secretIdSelector]) {
|
||||
[credentials setValue:secretId forKey:@"secretId"];
|
||||
}
|
||||
|
||||
SEL secretKeySelector = NSSelectorFromString(@"setSecretKey:");
|
||||
if ([credentials respondsToSelector:secretKeySelector]) {
|
||||
[credentials setValue:secretKey forKey:@"secretKey"];
|
||||
}
|
||||
|
||||
SEL selector = NSSelectorFromString(@"setupPermanentCredentials:");
|
||||
if ([_clsService respondsToSelector:selector]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[_clsService performSelector:selector withObject:credentials];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupCredentialsRefreshBlock:(QCloudCredential * _Nonnull (^)(void))refreshBlock {
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
if (refreshBlock) {
|
||||
SEL selector = NSSelectorFromString(@"setupCredentialsRefreshBlock:");
|
||||
if ([_clsService respondsToSelector:selector]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[_clsService performSelector:selector withObject:^id _Nonnull{
|
||||
|
||||
QCloudCredential *credential = refreshBlock();
|
||||
if (!credential) {
|
||||
return nil;
|
||||
}
|
||||
Class cla = NSClassFromString(@"QCloudClsSessionCredentials");
|
||||
if (!cla) {
|
||||
return nil;
|
||||
}
|
||||
id credentials = [[cla alloc]init];
|
||||
// 或更安全的版本(检查属性是否存在)
|
||||
SEL secretIdSelector = NSSelectorFromString(@"setSecretId:");
|
||||
if ([credentials respondsToSelector:secretIdSelector]) {
|
||||
[credentials setValue:credential.secretID forKey:@"secretId"];
|
||||
}
|
||||
|
||||
SEL secretKeySelector = NSSelectorFromString(@"setSecretKey:");
|
||||
if ([credentials respondsToSelector:secretKeySelector]) {
|
||||
[credentials setValue:credential.secretKey forKey:@"secretKey"];
|
||||
}
|
||||
|
||||
SEL tokenSelector = NSSelectorFromString(@"setToken:");
|
||||
if ([credentials respondsToSelector:tokenSelector]) {
|
||||
[credentials setValue:credential.token forKey:@"token"];
|
||||
}
|
||||
|
||||
SEL expiredTimeSelector = NSSelectorFromString(@"setExpiredTime:");
|
||||
if ([credentials respondsToSelector:expiredTimeSelector]) {
|
||||
[credentials setValue:@([credential.expirationDate timeIntervalSince1970]) forKey:@"expiredTime"];
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)appendLog:(QCloudLogModel * (^)(void))logCreate {
|
||||
QCloudWeakSelf(self);
|
||||
dispatch_async(_buildQueue, ^{
|
||||
QCloudStrongSelf(self);
|
||||
QCloudLogModel *log = logCreate();
|
||||
if (log.level <= [QCloudLogger sharedLogger].logClsLevel) {
|
||||
NSMutableDictionary *params = [NSMutableDictionary new];
|
||||
params[@"level"] = [QCloudLogModel descriptionForLogLevel:log.level]?:@"";
|
||||
params[@"category"] = [QCloudLogModel descriptionForLogCategory:log.category]?:@"";
|
||||
params[@"timestamp"] = @([log.date timeIntervalSince1970]).stringValue?:@"";
|
||||
params[@"threadName"] = log.threadName?:@"";
|
||||
params[@"tag"] = log.tag?:@"";
|
||||
params[@"message"] = log.message?:@"";
|
||||
params[@"deviceID"] = QCloudLogger.sharedLogger.deviceID?:@"";
|
||||
params[@"deviceModel"] = QCloudLogger.sharedLogger.deviceModel?:@"";
|
||||
params[@"appVersion"] = QCloudLogger.sharedLogger.appVersion?:@"";
|
||||
[QCloudLogger.sharedLogger.extendInfo enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if (obj && key) {
|
||||
[params setObject:obj forKey:key];
|
||||
}
|
||||
}];
|
||||
|
||||
// 动态调用 reportWithEventCode:params:
|
||||
SEL selector = NSSelectorFromString(@"reportWithEventCode:params:");
|
||||
if ([strongself.clsService respondsToSelector:selector]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[strongself.clsService performSelector:selector withObject:QCloudTrackCosSdkLog withObject:params];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCustomLoggerOutput.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCustomLoggerOutput.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudCustomLoggerOutput.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by garenwang on 2025/4/11.
|
||||
//
|
||||
|
||||
#import <QCloudCore/QCloudCore.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^QCloudCustomLoggerOutputCallBack)(QCloudLogModel * model,NSDictionary *extendInfo);
|
||||
@interface QCloudCustomLoggerOutput : QCloudLoggerOutput
|
||||
@property (nonatomic,strong)QCloudCustomLoggerOutputCallBack callback;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
45
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCustomLoggerOutput.m
generated
Normal file
45
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudCustomLoggerOutput.m
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// QCloudCustomLoggerOutput.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by garenwang on 2025/4/11.
|
||||
//
|
||||
|
||||
#import "QCloudCustomLoggerOutput.h"
|
||||
|
||||
@interface QCloudCustomLoggerOutput ()
|
||||
@property(strong,nonatomic)dispatch_queue_t buildQueue;
|
||||
@end
|
||||
|
||||
@implementation QCloudCustomLoggerOutput
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_buildQueue = dispatch_queue_create("com.tencent.qcloud.logger.cls.build", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)appendLog:(QCloudLogModel * (^)(void))logCreate {
|
||||
QCloudWeakSelf(self);
|
||||
dispatch_async(_buildQueue, ^{
|
||||
QCloudStrongSelf(self);
|
||||
QCloudLogModel *log = logCreate();
|
||||
NSMutableDictionary *params = QCloudLogger.sharedLogger.extendInfo?QCloudLogger.sharedLogger.extendInfo.mutableCopy:[NSMutableDictionary new];
|
||||
if (QCloudLogger.sharedLogger.deviceID) {
|
||||
params[@"deviceID"] = QCloudLogger.sharedLogger.deviceID;
|
||||
}
|
||||
if (QCloudLogger.sharedLogger.deviceModel) {
|
||||
params[@"deviceModel"] = QCloudLogger.sharedLogger.deviceModel;
|
||||
}
|
||||
if (QCloudLogger.sharedLogger.appVersion) {
|
||||
params[@"appVersion"] = QCloudLogger.sharedLogger.appVersion;
|
||||
}
|
||||
if (self.callback) {
|
||||
self.callback(log,params);
|
||||
}
|
||||
});
|
||||
}
|
||||
@end
|
||||
24
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileLogger.h
generated
Normal file
24
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileLogger.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// QCloudFileLogger.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudLoggerOutput.h"
|
||||
@class QCloudFileLogger;
|
||||
@protocol QCloudFileLoggerDelegate <NSObject>
|
||||
- (void)fileLoggerDidFull:(QCloudFileLogger *)logger;
|
||||
@end
|
||||
|
||||
@class QCloudLogModel;
|
||||
@interface QCloudFileLogger : QCloudLoggerOutput
|
||||
@property (nonatomic, weak) id<QCloudFileLoggerDelegate> delegate;
|
||||
@property (nonatomic, strong, readonly) NSString *path;
|
||||
@property (nonatomic, assign, readonly) uint64_t maxSize;
|
||||
@property (nonatomic, assign, readonly) uint64_t currentSize;
|
||||
@property (nonatomic, assign, readonly) BOOL isFull;
|
||||
- (instancetype)initWithPath:(NSString *)path maxSize:(uint64_t)maxSize;
|
||||
@end
|
||||
188
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileLogger.m
generated
Normal file
188
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileLogger.m
generated
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// QCloudFileLogger.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudFileLogger.h"
|
||||
#import "QCloudLogModel.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import <zlib.h>
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#import "QCloudSDKModuleManager.h"
|
||||
#import "NSObject+QCloudModel.h"
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
@interface QCloudFileLogger () {
|
||||
dispatch_source_t _timer;
|
||||
}
|
||||
@property (nonatomic, strong) dispatch_queue_t buildQueue;
|
||||
@property (nonatomic, strong) NSFileHandle *fileHandler;
|
||||
@property (nonatomic, strong) NSMutableData *sliceData;
|
||||
@property (nonatomic, assign) uint64_t sliceSize;
|
||||
@end
|
||||
|
||||
@implementation QCloudFileLogger
|
||||
@synthesize currentSize = _currentSize;
|
||||
- (void)commonInit {
|
||||
_buildQueue = dispatch_queue_create("com.tencent.qcloud.logger.build", DISPATCH_QUEUE_SERIAL);
|
||||
//
|
||||
_sliceSize = 200 * 1024;
|
||||
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _buildQueue);
|
||||
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC, 0);
|
||||
dispatch_source_set_event_handler(_timer, ^{
|
||||
[self writeCliceDataToFile];
|
||||
});
|
||||
_sliceData = [NSMutableData dataWithCapacity:(NSUInteger)_sliceSize];
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self writeCliceDataToFile];
|
||||
[_fileHandler closeFile];
|
||||
dispatch_source_cancel(_timer);
|
||||
}
|
||||
- (instancetype)initWithPath:(NSString *)path maxSize:(uint64_t)maxSize {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self commonInit];
|
||||
_maxSize = maxSize;
|
||||
_path = path;
|
||||
_currentSize = QCloudFileSize(path);
|
||||
if (!QCloudFileExist(path)) {
|
||||
[[NSFileManager defaultManager] createFileAtPath:path contents:[NSData data] attributes:nil];
|
||||
NSArray *allModules = [[QCloudSDKModuleManager shareInstance] allModules];
|
||||
NSData *modulestring = [allModules qcloud_modelToJSONData];
|
||||
[_sliceData appendData:modulestring];
|
||||
}
|
||||
dispatch_resume(_timer);
|
||||
_fileHandler = [NSFileHandle fileHandleForWritingAtPath:path];
|
||||
[_fileHandler seekToEndOfFile];
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(flushAllFiles)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(flushAllFiles)
|
||||
name:UIApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(flushAllFiles) name:UIApplicationWillTerminateNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(flushAllFiles)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
//
|
||||
#elif TARGET_OS_MAC
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(flushAllFiles)
|
||||
name:NSApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(flushAllFiles) name:NSApplicationWillTerminateNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(flushAllFiles) name:NSApplicationWillHideNotification object:nil];
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)flushAllFiles {
|
||||
dispatch_async(_buildQueue, ^{
|
||||
[self writeCliceDataToFile];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)writeCliceDataToFile {
|
||||
if (_sliceData.length) {
|
||||
@try {
|
||||
if ([QCloudLogger sharedLogger].aesKey.length>0 && [QCloudLogger sharedLogger].aesIv.length>0) {
|
||||
[self appendEncryptedLogToFile:_sliceData key:[QCloudLogger sharedLogger].aesKey iv:[QCloudLogger sharedLogger].aesIv];
|
||||
}else{
|
||||
[_fileHandler writeData:_sliceData];
|
||||
}
|
||||
_sliceData = [NSMutableData dataWithCapacity:(NSUInteger)_sliceSize];
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"no space left on device");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendLog:(QCloudLogModel * (^)(void))logCreate {
|
||||
dispatch_async(_buildQueue, ^{
|
||||
QCloudLogModel *log = logCreate();
|
||||
if (log.level <= [QCloudLogger sharedLogger].logFileLevel) {
|
||||
NSString *message = [NSString stringWithFormat:@"%@\n", [log fileDescription]];
|
||||
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
|
||||
self->_currentSize += data.length;
|
||||
[self->_sliceData appendData:data];
|
||||
//
|
||||
if (self.currentSize >= self.maxSize) {
|
||||
[self writeCliceDataToFile];
|
||||
if ([self.delegate respondsToSelector:@selector(fileLoggerDidFull:)]) {
|
||||
[self.delegate fileLoggerDidFull:self];
|
||||
}
|
||||
} else {
|
||||
if (self->_sliceData.length >= self->_sliceSize) {
|
||||
[self writeCliceDataToFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)isFull {
|
||||
return self.currentSize >= self.maxSize;
|
||||
}
|
||||
|
||||
// 加密单条日志并追加到文件
|
||||
- (BOOL)appendEncryptedLogToFile:(NSData *)messageData key:(NSData *)key iv:(NSData *)iv {
|
||||
NSData *encryptedData = [self encryptData:messageData key:key iv:iv];
|
||||
if (!encryptedData) return NO;
|
||||
if (_fileHandler) {
|
||||
// 写入长度头(4字节大端序)
|
||||
uint32_t length = (uint32_t)encryptedData.length;
|
||||
uint32_t lengthBE = htonl(length);
|
||||
[_fileHandler writeData:[NSData dataWithBytes:&lengthBE length:sizeof(lengthBE)]];
|
||||
|
||||
// 写入密文
|
||||
[_fileHandler writeData:encryptedData];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
#pragma mark - 加解密核心
|
||||
- (NSData *)encryptData:(NSData *)data key:(NSData *)key iv:(NSData *)iv{
|
||||
return [self cryptData:data operation:kCCEncrypt key:key iv:iv];
|
||||
}
|
||||
|
||||
- (NSData *)cryptData:(NSData *)data operation:(CCOperation)op key:(NSData *)key iv:(NSData *)iv {
|
||||
size_t bufferSize = data.length + kCCBlockSizeAES128;
|
||||
void *buffer = malloc(bufferSize);
|
||||
|
||||
size_t numBytesProcessed = 0;
|
||||
CCCryptorStatus status = CCCrypt(op,
|
||||
kCCAlgorithmAES,
|
||||
kCCOptionPKCS7Padding,
|
||||
key.bytes,
|
||||
kCCKeySizeAES256,
|
||||
iv.bytes,
|
||||
data.bytes,
|
||||
data.length,
|
||||
buffer,
|
||||
bufferSize,
|
||||
&numBytesProcessed);
|
||||
|
||||
if (status == kCCSuccess) {
|
||||
return [NSData dataWithBytesNoCopy:buffer length:numBytesProcessed];
|
||||
}else{
|
||||
NSLog(@"警告:日志加密失败");
|
||||
}
|
||||
free(buffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
14
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileZipper.h
generated
Normal file
14
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileZipper.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// QCloudFileZipper.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudFileZipper : NSObject
|
||||
- (instancetype)initWithInputFilePath:(NSString *)path;
|
||||
- (BOOL)outputToPath:(NSString *)path;
|
||||
@end
|
||||
28
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileZipper.m
generated
Normal file
28
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudFileZipper.m
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// QCloudFileZipper.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudFileZipper.h"
|
||||
|
||||
@interface QCloudFileZipper ()
|
||||
@property (nonatomic, strong) NSString *inputPath;
|
||||
@end
|
||||
|
||||
@implementation QCloudFileZipper
|
||||
- (instancetype)initWithInputFilePath:(NSString *)path {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_inputPath = path;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)outputToPath:(NSString *)path {
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
103
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogModel.h
generated
Normal file
103
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogModel.h
generated
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// QCloudLogModel.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
/**
|
||||
`QCloudLogLevel` enum specifies different levels of logging that could be used to limit or display more messages in logs.
|
||||
*/
|
||||
typedef NS_ENUM(uint8_t, QCloudLogLevel) {
|
||||
/**
|
||||
Log level that disables all logging.
|
||||
*/
|
||||
QCloudLogLevelNone = 1,
|
||||
/**
|
||||
Log level that if set is going to output error messages to the log.
|
||||
*/
|
||||
QCloudLogLevelError = 2,
|
||||
/**
|
||||
Log level that if set is going to output the following messages to log:
|
||||
- Errors
|
||||
- Warnings
|
||||
*/
|
||||
QCloudLogLevelWarning = 3,
|
||||
/**
|
||||
Log level that if set is going to output the following messages to log:
|
||||
- Errors
|
||||
- Warnings
|
||||
- Informational messages
|
||||
*/
|
||||
QCloudLogLevelInfo = 4,
|
||||
/**
|
||||
Log level that if set is going to output the following messages to log:
|
||||
- Errors
|
||||
- Warnings
|
||||
- Informational messages
|
||||
- Debug messages
|
||||
*/
|
||||
|
||||
QCloudLogLevelDebug = 5,
|
||||
/**
|
||||
Log level that if set is going to output the following messages to log:
|
||||
- Errors
|
||||
- Warnings
|
||||
- Informational messages
|
||||
- Debug messages
|
||||
- Verbose
|
||||
*/
|
||||
QCloudLogLevelVerbose = 6,
|
||||
|
||||
};
|
||||
|
||||
typedef NS_ENUM(uint8_t, QCloudLogCategory) {
|
||||
QCloudLogCategoryNone,
|
||||
/**
|
||||
操作过程日志(如上传分片开始、网络请求发起)
|
||||
*/
|
||||
QCloudLogCategoryProcess,
|
||||
/**
|
||||
操作结果日志(如上传成功、下载失败)
|
||||
*/
|
||||
QCloudLogCategoryResult,
|
||||
/**
|
||||
网络层日志(如请求、响应、http性能)
|
||||
*/
|
||||
QCloudLogCategoryNetwork,
|
||||
/**
|
||||
网络探测日志(如网络连接导致失败时的探测)
|
||||
*/
|
||||
QCloudLogCategoryProbe,
|
||||
/**
|
||||
错误堆栈日志(如异常捕获)
|
||||
*/
|
||||
QCloudLogCategoryError,
|
||||
};
|
||||
|
||||
@interface QCloudLogModel : NSObject
|
||||
@property (nonatomic, assign) QCloudLogLevel level;
|
||||
@property (nonatomic, strong) NSString *message;
|
||||
@property (nonatomic, assign) QCloudLogCategory category;
|
||||
@property (nonatomic, strong) NSString *tag;
|
||||
|
||||
@property (nonatomic, strong) NSDate *date;
|
||||
@property (nonatomic, strong) NSString *file;
|
||||
@property (nonatomic, assign) int line;
|
||||
@property (nonatomic, assign) BOOL simpleLog;
|
||||
@property (nonatomic, strong) NSString *funciton;
|
||||
@property (nonatomic, strong) NSString *threadName;
|
||||
|
||||
/**
|
||||
生成用于写文件的Log信息
|
||||
|
||||
@return 写入文件的Log信息
|
||||
*/
|
||||
- (NSString *)fileDescription;
|
||||
|
||||
+ (NSString *)descriptionForLogCategory:(QCloudLogCategory)logCategory;
|
||||
|
||||
+ (NSString *)descriptionForLogLevel:(QCloudLogLevel)logLevel;
|
||||
@end
|
||||
168
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogModel.m
generated
Normal file
168
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogModel.m
generated
Normal file
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// QCloudLogModel.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudLogModel.h"
|
||||
#import "NSDate+QCLOUD.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "NSObject+QCloudModel.h"
|
||||
@implementation QCloudLogModel
|
||||
///--------------------------------------
|
||||
#pragma mark - Logging Messages
|
||||
///--------------------------------------
|
||||
+ (NSString *)descriptionForLogLevel:(QCloudLogLevel)logLevel {
|
||||
NSString *description = nil;
|
||||
switch (logLevel) {
|
||||
case QCloudLogLevelNone:
|
||||
break;
|
||||
case QCloudLogLevelDebug:
|
||||
description = @"Debug";
|
||||
break;
|
||||
case QCloudLogLevelError:
|
||||
description = @"Error";
|
||||
break;
|
||||
case QCloudLogLevelWarning:
|
||||
description = @"Warning";
|
||||
break;
|
||||
case QCloudLogLevelInfo:
|
||||
description = @"Info";
|
||||
break;
|
||||
case QCloudLogLevelVerbose:
|
||||
description = @"Verbose";
|
||||
break;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
+ (NSString *)descriptionForLogCategory:(QCloudLogCategory)logCategory {
|
||||
NSString *description = nil;
|
||||
switch (logCategory) {
|
||||
case QCloudLogCategoryNone:
|
||||
description = @"";
|
||||
break;
|
||||
case QCloudLogCategoryProcess:
|
||||
description = @"PROCESS";
|
||||
break;
|
||||
case QCloudLogCategoryResult:
|
||||
description = @"RESULT";
|
||||
break;
|
||||
case QCloudLogCategoryNetwork:
|
||||
description = @"NETWORK";
|
||||
break;
|
||||
case QCloudLogCategoryProbe:
|
||||
description = @"PROBE";
|
||||
break;
|
||||
case QCloudLogCategoryError:
|
||||
description = @"ERROR";
|
||||
break;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
static BOOL willOutputColor = NO;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
char *xcodeColor = getenv("XcodeColors");
|
||||
if (xcodeColor && (strcmp(xcodeColor, "YES") == 0)) {
|
||||
willOutputColor = YES;
|
||||
setenv("XcodeColors", "YES", 0);
|
||||
}
|
||||
});
|
||||
|
||||
NSString * extInfo = @"";
|
||||
if ([QCloudLogger sharedLogger].extendInfo && !self.simpleLog) {
|
||||
extInfo = [NSString stringWithFormat:@",extendInfo=%@",[[QCloudLogger sharedLogger].extendInfo qcloud_modelToJSONString]];
|
||||
extInfo = [NSString stringWithFormat:@",appVersion=%@,deviceModel=%@,deviceID=%@",[QCloudLogger sharedLogger].appVersion,[QCloudLogger sharedLogger].deviceModel,[QCloudLogger sharedLogger].deviceID];
|
||||
self.message = [self.message stringByAppendingString:extInfo];
|
||||
}
|
||||
|
||||
NSString *description;
|
||||
if (self.simpleLog) {
|
||||
description = [NSMutableString stringWithFormat:@"%@ %@[%@][%@]%@", [NSDate qcloud_stringFromDate_24:self.date],self.tag,[QCloudLogModel descriptionForLogCategory:self.category],self.threadName,self.message];
|
||||
}else if (willOutputColor) {
|
||||
description = [NSMutableString stringWithFormat:@"%@%@/%@[%@][%@ %@]%@%@",[QCloudLogModel consoleLogColorWithLevel:self.level], [QCloudLogModel descriptionForLogLevel:self.level],[NSDate qcloud_stringFromDate_24:self.date],self.threadName,[QCloudLogModel descriptionForLogCategory:self.category],self.tag,[QCloudLogModel consoleLogColorWithLevel:self.level],self.message];
|
||||
|
||||
} else {
|
||||
description = [NSMutableString stringWithFormat:@"%@/%@[%@][%@ %@]%@", [QCloudLogModel descriptionForLogLevel:self.level],[NSDate qcloud_stringFromDate_24:self.date],self.threadName,[QCloudLogModel descriptionForLogCategory:self.category],self.tag,self.message];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
+ (NSString *)consoleLogColorWithLevel:(QCloudLogLevel)level {
|
||||
switch (level) {
|
||||
case QCloudLogLevelInfo:
|
||||
return @"🔷";
|
||||
case QCloudLogLevelNone:
|
||||
return @"";
|
||||
case QCloudLogLevelDebug:
|
||||
return @"◾️ ";
|
||||
case QCloudLogLevelError:
|
||||
return @"🛑";
|
||||
case QCloudLogLevelWarning:
|
||||
return @"🔶";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
+ (NSString *)consoleRestLogColorWithLevel:(QCloudLogLevel)level {
|
||||
switch (level) {
|
||||
case QCloudLogLevelInfo:
|
||||
return @"\033[fg0,0,0;\033[bg255,255,255;";
|
||||
case QCloudLogLevelNone:
|
||||
return @"\033[fg0,0,0;\033[bg255,255,255;";
|
||||
case QCloudLogLevelDebug:
|
||||
return @"\033[fg0,0,0;\033[bg255,255,255;";
|
||||
case QCloudLogLevelError:
|
||||
return @"\033[fg0,0,0;\033[bg200,0,0;";
|
||||
case QCloudLogLevelWarning:
|
||||
return @"\033[fg0,0,0;\033[bg100,100,100;";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
- (NSString *)fileLogColorWithLevel:(QCloudLogLevel)level {
|
||||
switch (level) {
|
||||
case QCloudLogLevelInfo:
|
||||
return @"\e[38;5;38;82m";
|
||||
case QCloudLogLevelNone:
|
||||
return @"\e[0m";
|
||||
case QCloudLogLevelDebug:
|
||||
return @"\e[30;48;5;50m";
|
||||
case QCloudLogLevelError:
|
||||
return @"\e[41;41;41;256m";
|
||||
case QCloudLogLevelWarning:
|
||||
return @"\e[38;5;251;203m";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
- (NSString *)fileDescription {
|
||||
NSString *color = [self fileLogColorWithLevel:self.level];
|
||||
NSMutableString *log = [NSMutableString new];
|
||||
[log appendString:color];
|
||||
[log appendFormat:@"[%@]", self.date];
|
||||
[log appendFormat:@"[%@]", [QCloudLogModel descriptionForLogLevel:self.level]];
|
||||
[log appendString:@"\e[0m"];
|
||||
if (self.file.length) {
|
||||
[log appendFormat:@"[%@]", [self.file componentsSeparatedByString:@"/"].lastObject];
|
||||
}
|
||||
if (self.funciton.length) {
|
||||
[log appendFormat:@"[%@]", self.funciton];
|
||||
}
|
||||
if (self.line > 0) {
|
||||
[log appendFormat:@"[%d]", self.line];
|
||||
}
|
||||
[log appendString:self.message];
|
||||
return log;
|
||||
}
|
||||
|
||||
@end
|
||||
154
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogger.h
generated
Normal file
154
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogger.h
generated
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// QCloudLogger.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudLogModel.h"
|
||||
#import "QCloudLoggerOutput.h"
|
||||
|
||||
#define QCloudLog(level, c, t, frmt, ...) \
|
||||
[[QCloudLogger sharedLogger] logMessageWithLevel:level category:c tag:t cmd:__PRETTY_FUNCTION__ line:__LINE__ file:__FILE__ format:(frmt), ##__VA_ARGS__]
|
||||
|
||||
#define QCloudLogError(frmt, ...) QCloudLog(QCloudLogLevelError, QCloudLogCategoryNone, @"", (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarning(frmt, ...) QCloudLog(QCloudLogLevelWarning, QCloudLogCategoryNone, @"", (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfo(frmt, ...) QCloudLog(QCloudLogLevelInfo, QCloudLogCategoryNone, @"", (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebug(frmt, ...) QCloudLog(QCloudLogLevelDebug, QCloudLogCategoryNone, @"", (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerbose(frmt, ...) QCloudLog(QCloudLogLevelVerbose, QCloudLogCategoryNone, @"", (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogErrorP(tag,frmt, ...) QCloudLog(QCloudLogLevelError,QCloudLogCategoryProcess,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarningP(tag,frmt, ...) QCloudLog(QCloudLogLevelWarning,QCloudLogCategoryProcess,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfoP(tag,frmt, ...) QCloudLog(QCloudLogLevelInfo,QCloudLogCategoryProcess,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebugP(tag,frmt, ...) QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryProcess,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerboseP(tag,frmt, ...) QCloudLog(QCloudLogLevelVerbose,QCloudLogCategoryProcess,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogErrorR(tag,frmt, ...) QCloudLog(QCloudLogLevelError,QCloudLogCategoryResult,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarningR(tag,frmt, ...) QCloudLog(QCloudLogLevelWarning,QCloudLogCategoryResult,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfoR(tag,frmt, ...) QCloudLog(QCloudLogLevelInfo,QCloudLogCategoryResult,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebugR(tag,frmt, ...) QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryResult,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerboseR(tag,frmt, ...) QCloudLog(QCloudLogLevelVerbose,QCloudLogCategoryResult,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogErrorN(tag,frmt, ...) QCloudLog(QCloudLogLevelError,QCloudLogCategoryNetwork,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarningN(tag,frmt, ...) QCloudLog(QCloudLogLevelWarning,QCloudLogCategoryNetwork,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfoN(tag,frmt, ...) QCloudLog(QCloudLogLevelInfo,QCloudLogCategoryNetwork,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebugN(tag,frmt, ...) QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryNetwork,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerboseN(tag,frmt, ...) QCloudLog(QCloudLogLevelVerbose,QCloudLogCategoryNetwork,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogErrorPB(tag,frmt, ...) QCloudLog(QCloudLogLevelError,QCloudLogCategoryProbe,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarningPB(tag,frmt, ...) QCloudLog(QCloudLogLevelWarning,QCloudLogCategoryProbe,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfoPB(tag,frmt, ...) QCloudLog(QCloudLogLevelInfo,QCloudLogCategoryProbe,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebugPB(tag,frmt, ...) QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryProbe,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerbosePB(tag,frmt, ...) QCloudLog(QCloudLogLevelVerbose,QCloudLogCategoryProbe,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogErrorE(tag,frmt, ...) QCloudLog(QCloudLogLevelError,QCloudLogCategoryError,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogWarningE(tag,frmt, ...) QCloudLog(QCloudLogLevelWarning,QCloudLogCategoryError,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogInfoE(tag,frmt, ...) QCloudLog(QCloudLogLevelInfo,QCloudLogCategoryError,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogDebugE(tag,frmt, ...) QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryError,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
#define QCloudLogVerboseE(tag,frmt, ...) QCloudLog(QCloudLogLevelVerbose,QCloudLogCategoryError,tag, (frmt), ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define QCloudLogException(exception) \
|
||||
QCloudLogError(@"",@"Caught \"%@\" with reason \"%@\"%@", exception.name, exception, \
|
||||
[exception callStackSymbols] ? [NSString stringWithFormat:@":\n%@.", [exception callStackSymbols]] : @"")
|
||||
|
||||
#define QCloudLogTrance() QCloudLog(QCloudLogLevelDebug,QCloudLogCategoryNone,@"", @"%@", [NSThread callStackSymbols])
|
||||
|
||||
@interface QCloudLogger : NSObject
|
||||
|
||||
|
||||
/// 日志加密Key,不指定则不加密日志。
|
||||
@property (nonatomic, strong) NSData *aesKey;
|
||||
|
||||
/// 日志加密IV,不指定则不加密日志。
|
||||
@property (nonatomic, strong) NSData *aesIv;
|
||||
|
||||
/// 扩展信息,用于日志上报
|
||||
@property (nonatomic, strong) NSDictionary *extendInfo;
|
||||
|
||||
/// 设备ID
|
||||
@property (nonatomic, strong) NSString *deviceID;
|
||||
|
||||
/// 机型
|
||||
@property (nonatomic, strong) NSString *deviceModel;
|
||||
|
||||
/// APP版本
|
||||
@property (nonatomic, strong) NSString *appVersion;
|
||||
|
||||
/// 控制台输出的日志级别
|
||||
@property (nonatomic, assign) QCloudLogLevel logLevel;
|
||||
|
||||
@property (nonatomic, assign) QCloudLogLevel logFileLevel;
|
||||
|
||||
@property (nonatomic, assign) QCloudLogLevel logClsLevel;
|
||||
|
||||
/// 本地日志路径
|
||||
@property (nonatomic, strong, readonly) NSString *logDirctoryPath;
|
||||
|
||||
@property (nonatomic, assign) uint64_t maxStoarageSize;
|
||||
|
||||
/// 日志保存天数
|
||||
@property (nonatomic, assign) float keepDays;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Shared Logger
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
A shared instance of `QCloudLogger` that should be used for all logging.
|
||||
|
||||
@return An shared singleton instance of `QCloudLogger`.
|
||||
*/
|
||||
+ (instancetype)sharedLogger;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Logging Messages
|
||||
///--------------------------------------
|
||||
|
||||
- (void)logMessageWithLevel:(QCloudLogLevel)level category:(QCloudLogCategory)category tag:(NSString *)tag cmd:(const char *)commandInfo line:(int)line file:(const char *)file format:(NSString *)format, ...;
|
||||
|
||||
/**
|
||||
增加一个输出源
|
||||
|
||||
@param output 输出源
|
||||
*/
|
||||
- (void)addLogger:(QCloudLoggerOutput *)output;
|
||||
|
||||
/**
|
||||
删除一个输出源
|
||||
|
||||
@param output 删除一个输出源
|
||||
*/
|
||||
- (void)removeLogger:(QCloudLoggerOutput *)output;
|
||||
@end
|
||||
274
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogger.m
generated
Normal file
274
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLogger.m
generated
Normal file
@@ -0,0 +1,274 @@
|
||||
//
|
||||
// QCloudLogger.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudLogger.h"
|
||||
#import "QCloudLogModel.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
#import <time.h>
|
||||
#import <xlocale.h>
|
||||
#import "QCloudFileLogger.h"
|
||||
#import "NSDate+QCLOUD.h"
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#define QCloudEachLogFileSize 10 * 1024 * 1024
|
||||
|
||||
@interface NSDate (QCloudEasy)
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSDate (QCloudEasy)
|
||||
|
||||
- (NSString *)qcloud_string {
|
||||
time_t pubdate = [self timeIntervalSince1970];
|
||||
struct tm *cTime = localtime(&pubdate);
|
||||
return [NSString stringWithFormat:@"%d-%02d-%02d-%02d-%02d-%02d", 1900 + cTime->tm_year, 1 + cTime->tm_mon, cTime->tm_mday, cTime->tm_hour,
|
||||
cTime->tm_min, cTime->tm_sec];
|
||||
}
|
||||
|
||||
+ (NSString *)qcloud_todayString {
|
||||
time_t pubdate = [[NSDate date] timeIntervalSince1970];
|
||||
struct tm *cTime = localtime(&pubdate);
|
||||
return [NSString stringWithFormat:@"%d-%02d-%02d", 1900 + cTime->tm_year, 1 + cTime->tm_mon, cTime->tm_mday];
|
||||
}
|
||||
|
||||
+ (NSDate *)qcloud_dateWithString:(NSString *)str {
|
||||
struct tm sometime;
|
||||
const char *formatString = "%Y-%m-%d";
|
||||
(void)strptime_l([str UTF8String], formatString, &sometime, NULL);
|
||||
return [NSDate dateWithTimeIntervalSince1970:mktime(&sometime)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NSString *const kQCloudLogExtension = @"log";
|
||||
@interface QCloudLogger () <QCloudFileLoggerDelegate>
|
||||
@property (nonatomic, strong) QCloudFileLogger *currentFileLogger;
|
||||
@end
|
||||
|
||||
@implementation QCloudLogger {
|
||||
NSMutableArray *_loggerOutputs;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedLogger {
|
||||
static QCloudLogger *logger = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
logger = [QCloudLogger new];
|
||||
char *level = getenv("QCloudLogLevel");
|
||||
if (NULL != level && strlen(level) > 0) {
|
||||
int logLevel = atoi(level);
|
||||
if (logLevel >= QCloudLogLevelNone && logLevel <= QCloudLogLevelVerbose) {
|
||||
logger.logLevel = logLevel;
|
||||
} else {
|
||||
logger.logLevel = QCloudLogLevelVerbose;
|
||||
}
|
||||
} else {
|
||||
logger.logLevel = QCloudLogLevelVerbose;
|
||||
}
|
||||
});
|
||||
logger.logFileLevel = QCloudLogLevelVerbose;
|
||||
logger.logClsLevel = QCloudLogLevelVerbose;
|
||||
return logger;
|
||||
}
|
||||
|
||||
- (NSDictionary *)extendInfo{
|
||||
if (![_extendInfo objectForKey:@"qcloud_platform"]) {
|
||||
NSMutableDictionary *mextendInfo = _extendInfo?[_extendInfo mutableCopy]:[NSMutableDictionary new];
|
||||
[mextendInfo setObject:@"iOS" forKey:@"qcloud_platform"];
|
||||
_extendInfo = mextendInfo.copy;
|
||||
}
|
||||
return _extendInfo;
|
||||
}
|
||||
|
||||
-(NSMutableArray *)loggerOutputs{
|
||||
if (!_loggerOutputs) {
|
||||
_loggerOutputs = [NSMutableArray new];
|
||||
_currentFileLogger = [[QCloudFileLogger alloc] initWithPath:[self avilableLogFilePath] maxSize:QCloudEachLogFileSize];
|
||||
_currentFileLogger.delegate = self;
|
||||
[_loggerOutputs addObject:_currentFileLogger];
|
||||
}
|
||||
return _loggerOutputs;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_maxStoarageSize = 70 * 1024 * 1024;
|
||||
_keepDays = 3;
|
||||
//
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(tryCleanLogs)
|
||||
name:UIApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tryCleanLogs) name:UIApplicationWillTerminateNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(tryCleanLogs)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
#elif TARGET_OS_MAC
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(tryCleanLogs)
|
||||
name:NSApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tryCleanLogs) name:NSApplicationWillTerminateNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tryCleanLogs) name:NSApplicationWillHideNotification object:nil];
|
||||
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)allLogFiles {
|
||||
NSString *logDir = self.logDirctoryPath;
|
||||
NSDirectoryEnumerator<NSString *> *enumertor = [[NSFileManager defaultManager] enumeratorAtPath:logDir];
|
||||
NSString *file = nil;
|
||||
NSMutableArray *files = [NSMutableArray new];
|
||||
while (file = [enumertor nextObject]) {
|
||||
if ([file.pathExtension isEqualToString:kQCloudLogExtension]) {
|
||||
[files addObject:file];
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
- (void)fileLoggerDidFull:(QCloudFileLogger *)logger {
|
||||
if (logger != _currentFileLogger) {
|
||||
return;
|
||||
}
|
||||
NSString *nextLogPath = [self avilableLogFilePath];
|
||||
if (_currentFileLogger.isFull) {
|
||||
QCloudFileLogger *fileLogger = [[QCloudFileLogger alloc] initWithPath:nextLogPath maxSize:QCloudEachLogFileSize];
|
||||
fileLogger.delegate = self;
|
||||
_currentFileLogger = fileLogger;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)avilableLogFilePath {
|
||||
NSArray *allLogFiles = [self allLogFiles];
|
||||
allLogFiles = [allLogFiles sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
|
||||
return [obj1 compare:obj2];
|
||||
}];
|
||||
NSString *lastLog = allLogFiles.lastObject;
|
||||
NSString *todayLogPrefix = [NSDate qcloud_todayString];
|
||||
NSString *readyLogName = [[NSDate date] qcloud_string];
|
||||
NSString *logName = nil;
|
||||
|
||||
NSString *lastLogPath = nil;
|
||||
if ([QCloudLogger sharedLogger].aesKey.length &&[QCloudLogger sharedLogger].aesKey.length) {
|
||||
readyLogName = [readyLogName stringByAppendingString:@"_encrypt"];
|
||||
if (![lastLog containsString:@"encrypt"]) {
|
||||
lastLog = nil;
|
||||
}
|
||||
}else{
|
||||
if ([lastLog containsString:@"encrypt"]) {
|
||||
lastLog = nil;
|
||||
}
|
||||
}
|
||||
if (lastLog) {
|
||||
lastLogPath = QCloudPathJoin(self.logDirctoryPath, lastLog);
|
||||
}
|
||||
if (!lastLog) {
|
||||
logName = [readyLogName stringByAppendingPathExtension:kQCloudLogExtension];
|
||||
} else {
|
||||
if ([lastLog hasPrefix:todayLogPrefix]) {
|
||||
if (QCloudFileSize(lastLogPath) >= QCloudEachLogFileSize) {
|
||||
logName = [readyLogName stringByAppendingPathExtension:kQCloudLogExtension];
|
||||
} else {
|
||||
logName = lastLog;
|
||||
}
|
||||
} else {
|
||||
logName = [readyLogName stringByAppendingPathExtension:kQCloudLogExtension];
|
||||
}
|
||||
}
|
||||
return QCloudPathJoin(self.logDirctoryPath, logName);
|
||||
}
|
||||
|
||||
- (void)tryCleanLogs {
|
||||
NSArray *allLogFiles = [self allLogFiles];
|
||||
allLogFiles = [allLogFiles sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
|
||||
return [obj2 compare:obj1];
|
||||
}];
|
||||
|
||||
NSString *logDir = self.logDirctoryPath;
|
||||
uint64_t totalSize = 0;
|
||||
NSString *agoDateString = [[NSDate dateWithTimeIntervalSinceNow:-self.keepDays * 24 * 60 * 60] qcloud_string];
|
||||
|
||||
for (NSString *logName in allLogFiles) {
|
||||
NSString *path = QCloudPathJoin(logDir, logName);
|
||||
totalSize += QCloudFileSize(path);
|
||||
NSString *dateString = [[logName stringByDeletingPathExtension] componentsSeparatedByString:@"_"].firstObject;
|
||||
|
||||
if (totalSize > self.maxStoarageSize || [dateString compare:agoDateString] == NSOrderedAscending) {
|
||||
QCloudRemoveFileByPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)logDirctoryPath {
|
||||
NSString *path = QCloudPathJoin(QCloudPathJoin(QCloudPathJoin(QCloudApplicationLibaryPath(), @"Caches"), @"qcloud"), @"logs");
|
||||
QCloudEnsurePathExist(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void)logMessageWithLevel:(QCloudLogLevel)level
|
||||
category:(QCloudLogCategory)category
|
||||
tag:(NSString *)tag
|
||||
cmd:(const char *)cmd
|
||||
line:(int)line
|
||||
file:(const char *)file
|
||||
format:(NSString *)format, ... NS_FORMAT_FUNCTION(7, 8) {
|
||||
if (level == QCloudLogLevelNone || !format) {
|
||||
return;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
QCloudLogModel * (^CreateLog)(void) = ^(void) {
|
||||
QCloudLogModel *log = [QCloudLogModel new];
|
||||
log.message = message;
|
||||
log.date = [NSDate localDate];
|
||||
log.level = level;
|
||||
log.funciton = [NSString stringWithCString:cmd encoding:NSUTF8StringEncoding];
|
||||
log.file = [NSString stringWithCString:file encoding:NSUTF8StringEncoding];
|
||||
log.line = line;
|
||||
log.category = category;
|
||||
log.tag = tag;
|
||||
if ([NSThread currentThread].name.length>0) {
|
||||
log.threadName = [NSThread currentThread].name;
|
||||
}else {
|
||||
log.threadName = [NSThread isMainThread]?@"main thread":@"child thread";
|
||||
}
|
||||
return log;
|
||||
};
|
||||
if (level <= self.logLevel) {
|
||||
QCloudLogModel *model = CreateLog();
|
||||
model.simpleLog = YES;
|
||||
NSLog(@"%@", [model debugDescription]);
|
||||
}
|
||||
for (QCloudLoggerOutput *output in self.loggerOutputs) {
|
||||
[output appendLog:CreateLog];
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
- (void)addLogger:(QCloudLoggerOutput *)output {
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
[self.loggerOutputs addObject:output];
|
||||
}
|
||||
|
||||
- (void)removeLogger:(QCloudLoggerOutput *)output {
|
||||
[self.loggerOutputs removeObject:output];
|
||||
}
|
||||
@end
|
||||
13
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLoggerOutput.h
generated
Normal file
13
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLoggerOutput.h
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// QCloudLoggerOutput.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by Dong Zhao on 2018/5/29.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudLogModel.h"
|
||||
@class QCloudLogModel;
|
||||
@interface QCloudLoggerOutput : NSObject
|
||||
- (void)appendLog:(QCloudLogModel * (^)(void))logCreate;
|
||||
@end
|
||||
15
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLoggerOutput.m
generated
Normal file
15
Pods/QCloudCore/QCloudCore/Classes/Base/Logger/QCloudLoggerOutput.m
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// QCloudLoggerOutput.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by Dong Zhao on 2018/5/29.
|
||||
//
|
||||
|
||||
#import "QCloudLoggerOutput.h"
|
||||
#import "QCloudLogger.h"
|
||||
@implementation QCloudLoggerOutput
|
||||
|
||||
- (void)appendLog:(QCloudLogModel * (^)(void))logCreate {
|
||||
[NSException exceptionWithName:@"com.qcloud.logger" reason:@"You must implementation this method in subclass of QCloudLoggerOutput" userInfo:nil];
|
||||
}
|
||||
@end
|
||||
28
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudMultiDelegateProxy.h
generated
Normal file
28
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudMultiDelegateProxy.h
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// QCloudMultiDelegateProxy.h
|
||||
// TACAuthorization
|
||||
//
|
||||
// Created by Dong Zhao on 2017/12/11.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
解决delegate多次转发的问题,通常情况下绝大部分SDK的delegate为一个属性,也就是说只能接受一个delegate,但是当SDK容纳了非常多的场景逻辑的时候,这个时候实现delegate协议的地方就非常臃肿难以拆分。所以设计了这个基础机制,可以将delegate向多个多想转发消息。这是一个1对多的转发代理。
|
||||
*/
|
||||
@interface QCloudMultiDelegateProxy<Type> : NSObject
|
||||
|
||||
/**
|
||||
添加一个接受转发的委托
|
||||
@note 在内部对该对象为弱应用,不用担心会产生内存问题。同时在编程的时候,也不要认为我们会持有该对象。
|
||||
@param delegate 接受转发的对象
|
||||
*/
|
||||
- (void)addDelegate:(Type)delegate;
|
||||
|
||||
/**
|
||||
删除一个接受转发的委托
|
||||
|
||||
@param delegate 将要被删除的对象
|
||||
*/
|
||||
- (void)removeDelegate:(Type)delegate;
|
||||
@end
|
||||
106
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudMultiDelegateProxy.m
generated
Normal file
106
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudMultiDelegateProxy.m
generated
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// QCloudMultiDelegateProxy.m
|
||||
// TACAuthorization
|
||||
//
|
||||
// Created by Dong Zhao on 2017/12/11.
|
||||
//
|
||||
|
||||
#import "QCloudMultiDelegateProxy.h"
|
||||
@interface QCloudMultiDelegateProxy ()
|
||||
@property (nonatomic, strong) NSRecursiveLock *lock;
|
||||
@property (nonatomic, strong) NSPointerArray *delegates;
|
||||
@end
|
||||
@implementation QCloudMultiDelegateProxy
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_delegates = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsWeakMemory];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addDelegate:(id)delegate {
|
||||
[_lock lock];
|
||||
NSUInteger index = NSNotFound;
|
||||
for (NSUInteger i = 0; i < _delegates.count; i++) {
|
||||
id d = [_delegates pointerAtIndex:i];
|
||||
if (d == delegate) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (index == NSNotFound) {
|
||||
[_delegates addPointer:(void *)delegate];
|
||||
}
|
||||
[_delegates compact];
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)removeDelegate:(id)delegate {
|
||||
[_lock lock];
|
||||
NSUInteger index = NSNotFound;
|
||||
for (NSUInteger i = 0; i < _delegates.count; i++) {
|
||||
id d = [_delegates pointerAtIndex:i];
|
||||
if (d == delegate) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (index != NSNotFound) {
|
||||
[_delegates removePointerAtIndex:index];
|
||||
}
|
||||
[_delegates compact];
|
||||
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (id)forwardingTargetForSelector:(SEL)sel {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInvocation *)_copyInvocation:(NSInvocation *)invocation {
|
||||
NSInvocation *copy = [NSInvocation invocationWithMethodSignature:[invocation methodSignature]];
|
||||
NSUInteger argCount = [[invocation methodSignature] numberOfArguments];
|
||||
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
char buffer[sizeof(intmax_t)];
|
||||
[invocation getArgument:(void *)&buffer atIndex:i];
|
||||
[copy setArgument:(void *)&buffer atIndex:i];
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation {
|
||||
[_delegates compact];
|
||||
for (NSUInteger index = 0; index < _delegates.count; index++) {
|
||||
id object = [_delegates pointerAtIndex:index];
|
||||
if ([object respondsToSelector:invocation.selector]) {
|
||||
NSInvocation *invocationCopy = [self _copyInvocation:invocation];
|
||||
[invocationCopy invokeWithTarget:object];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
|
||||
[_delegates compact];
|
||||
for (NSUInteger index = 0; index < _delegates.count; index++) {
|
||||
id object = [_delegates pointerAtIndex:index];
|
||||
if (object) {
|
||||
id result = [object methodSignatureForSelector:sel];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
[_delegates compact];
|
||||
for (NSUInteger index = 0; index < _delegates.count; index++) {
|
||||
id object = [_delegates pointerAtIndex:index];
|
||||
if ([object respondsToSelector:aSelector]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
35
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudWeakProxy.h
generated
Normal file
35
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudWeakProxy.h
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// QCloudWeakProxy.h
|
||||
// Pods
|
||||
//
|
||||
// Created by QCloudTernimalLab on 2016/11/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@interface QCloudWeakProxy : NSProxy
|
||||
/**
|
||||
The proxy target.
|
||||
*/
|
||||
@property (nullable, nonatomic, weak, readonly) id target;
|
||||
|
||||
/**
|
||||
Creates a new weak proxy for target.
|
||||
|
||||
@param target Target object.
|
||||
|
||||
@return A new proxy object.
|
||||
*/
|
||||
- (instancetype)initWithTarget:(id)target;
|
||||
|
||||
/**
|
||||
Creates a new weak proxy for target.
|
||||
|
||||
@param target Target object.
|
||||
|
||||
@return A new proxy object.
|
||||
*/
|
||||
+ (instancetype)proxyWithTarget:(id)target;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
88
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudWeakProxy.m
generated
Executable file
88
Pods/QCloudCore/QCloudCore/Classes/Base/ObjectFack/QCloudWeakProxy.m
generated
Executable file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// QCloudWeakProxy.m
|
||||
// Pods
|
||||
//
|
||||
// Created by QCloudTernimalLab on 2016/11/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudWeakProxy.h"
|
||||
|
||||
@implementation QCloudWeakProxy
|
||||
- (instancetype)initWithTarget:(id)target {
|
||||
_target = target;
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)proxyWithTarget:(id)target {
|
||||
if ([target isKindOfClass:[QCloudWeakProxy class]]) {
|
||||
return target;
|
||||
}
|
||||
return [[QCloudWeakProxy alloc] initWithTarget:target];
|
||||
}
|
||||
|
||||
- (id)forwardingTargetForSelector:(SEL)selector {
|
||||
return _target;
|
||||
}
|
||||
|
||||
/**
|
||||
防止crash:因为target是weak,随时会被释放如果target被释放了,又去调用了其他的方法快消息转发就会返回nil,这个时候就会进入慢消息转发,如果慢消息转发没实现,就会crash
|
||||
|
||||
*/
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation {
|
||||
void *null = NULL;
|
||||
[invocation setReturnValue:&null];
|
||||
}
|
||||
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
|
||||
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
return [_target respondsToSelector:aSelector];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
return [_target isEqual:object];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [_target hash];
|
||||
}
|
||||
|
||||
- (Class)superclass {
|
||||
return [_target superclass];
|
||||
}
|
||||
|
||||
- (Class)class {
|
||||
return [_target class];
|
||||
}
|
||||
|
||||
- (BOOL)isKindOfClass:(Class)aClass {
|
||||
if (aClass == [QCloudWeakProxy class]) {
|
||||
return YES;
|
||||
}
|
||||
return [_target isKindOfClass:aClass];
|
||||
}
|
||||
|
||||
- (BOOL)isMemberOfClass:(Class)aClass {
|
||||
return [_target isMemberOfClass:aClass];
|
||||
}
|
||||
|
||||
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
|
||||
return [_target conformsToProtocol:aProtocol];
|
||||
}
|
||||
|
||||
- (BOOL)isProxy {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [_target description];
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
return [_target debugDescription];
|
||||
}
|
||||
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudBundlePath.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudBundlePath.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudBundlePath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalAdjustablePath.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudBundlePath : QCloudUniversalAdjustablePath
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudBundlePath.m
generated
Normal file
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudBundlePath.m
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// QCloudBundlePath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudBundlePath.h"
|
||||
|
||||
@implementation QCloudBundlePath
|
||||
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudMediaPath.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudMediaPath.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudMediaPath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalFixedPath.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudMediaPath : QCloudUniversalFixedPath
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudMediaPath.m
generated
Normal file
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudMediaPath.m
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// QCloudMediaPath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudMediaPath.h"
|
||||
|
||||
@implementation QCloudMediaPath
|
||||
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudSandboxPath.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudSandboxPath.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudSandboxPath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalAdjustablePath.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudSandboxPath : QCloudUniversalAdjustablePath
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
15
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudSandboxPath.m
generated
Normal file
15
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudSandboxPath.m
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// QCloudSandboxPath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudSandboxPath.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
@implementation QCloudSandboxPath
|
||||
- (NSURL *)fileURL {
|
||||
NSString *restoredPath = QCloudGenerateLocalPath(self.originURL);
|
||||
return [NSURL fileURLWithPath:restoredPath];
|
||||
}
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalAdjustablePath.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalAdjustablePath.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudUniversalAdjustablePath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import <QCloudCore/QCloudCore.h>
|
||||
#import "QCloudUniversalPath.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudUniversalAdjustablePath : QCloudUniversalPath
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalAdjustablePath.m
generated
Normal file
12
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalAdjustablePath.m
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// QCloudUniversalAdjustablePath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalAdjustablePath.h"
|
||||
|
||||
@implementation QCloudUniversalAdjustablePath
|
||||
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalFixedPath.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalFixedPath.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudUniversalFixedPath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import <QCloudCore/QCloudCore.h>
|
||||
#import "QCloudUniversalPath.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudUniversalFixedPath : QCloudUniversalPath
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalFixedPath.m
generated
Normal file
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalFixedPath.m
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// QCloudUniversalFixedPath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalFixedPath.h"
|
||||
|
||||
@implementation QCloudUniversalFixedPath
|
||||
- (NSURL *)fileURL {
|
||||
if ([self.originURL hasPrefix:@"file:///"]) {
|
||||
return [NSURL URLWithString:self.originURL];
|
||||
}else{
|
||||
return [NSURL fileURLWithPath:self.originURL];
|
||||
}
|
||||
}
|
||||
@end
|
||||
21
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPath.h
generated
Normal file
21
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPath.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// QCloudUniversalPath.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudUniversalPathConstants.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudUniversalPath : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSString *originURL;
|
||||
@property (nonatomic, assign) QCloudUniversalPathType type;
|
||||
- (NSURL *)fileURL;
|
||||
- (instancetype)initWithStrippedURL:(NSString *)strippedURL;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
22
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPath.m
generated
Normal file
22
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPath.m
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// QCloudUniversalPath.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalPath.h"
|
||||
#import <QCloudCore/QCloudCore.h>
|
||||
|
||||
@implementation QCloudUniversalPath
|
||||
- (instancetype)initWithStrippedURL:(NSString *)strippedURL {
|
||||
self = [super init];
|
||||
_originURL = strippedURL;
|
||||
return self;
|
||||
}
|
||||
- (NSURL *)fileURL {
|
||||
@throw [NSException exceptionWithName:QCloudErrorDomain
|
||||
reason:[NSString stringWithFormat:@"不支持该路径下的文件续传:%@", _originURL]
|
||||
userInfo:nil];
|
||||
}
|
||||
@end
|
||||
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathConstants.h
generated
Normal file
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathConstants.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// QCloudUniversalPathConstants.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/21.
|
||||
//
|
||||
|
||||
#ifndef QCloudUniversalPathConstants_h
|
||||
#define QCloudUniversalPathConstants_h
|
||||
typedef NS_ENUM(NSInteger, QCloudUniversalPathType) {
|
||||
QCLOUD_UNIVERSAL_PATH_TYPE_FIXED,
|
||||
QCLOUD_UNIVERSAL_PATH_TYPE_ADJUSTABLE,
|
||||
QCLOUD_UNIVERSAL_PATH_TYPE_BUNDLE,
|
||||
QCLOUD_UNIVERSAL_PATH_TYPE_MEDIA,
|
||||
QCLOUD_UNIVERSAL_PATH_TYPE_SANDBOX
|
||||
};
|
||||
|
||||
#endif /* QCloudUniversalPathConstants_h */
|
||||
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathFactory.h
generated
Normal file
18
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathFactory.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// QCloudUniversalPathFactory.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudUniversalPath.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QCloudUniversalPathFactory : NSObject
|
||||
|
||||
+ (QCloudUniversalPath *)universalPathWithURL:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
69
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathFactory.m
generated
Normal file
69
Pods/QCloudCore/QCloudCore/Classes/Base/PathUtilities/QCloudUniversalPathFactory.m
generated
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// QCloudUniversalPathFactory.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by erichmzhang(张恒铭) on 2018/7/20.
|
||||
//
|
||||
|
||||
#import "QCloudUniversalPathFactory.h"
|
||||
#import "QCloudMediaPath.h"
|
||||
#import "QCloudSandboxPath.h"
|
||||
#import "QCloudBundlePath.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
|
||||
NSString *const kMediaURLPrefix = @"/var/mobile/Media/DCIM";
|
||||
#define kBundlePath [NSBundle mainBundle].bundlePath
|
||||
@interface NSString (UniversalPathExtension)
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (UniversalPathExtension)
|
||||
|
||||
- (BOOL)isBundlePath {
|
||||
return [self containsString:kBundlePath];
|
||||
}
|
||||
|
||||
- (BOOL)isSandboxPath {
|
||||
return [self containsString:QCloudApplicationDirectory()];
|
||||
}
|
||||
|
||||
- (BOOL)isMediaPath {
|
||||
return [self containsString:kMediaURLPrefix];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation QCloudUniversalPathFactory
|
||||
+ (QCloudUniversalPath *)universalPathWithURL:(NSURL *)url {
|
||||
QCloudUniversalPath *result;
|
||||
NSString *strippedURL;
|
||||
NSString *absoluteString = url.absoluteString;
|
||||
if (!url && ![url isKindOfClass:NSURL.class]) {
|
||||
QCloudLogDebugE(@"Utils",@"Nil paramater url!");
|
||||
return nil;
|
||||
}
|
||||
if ([absoluteString isMediaPath]) {
|
||||
strippedURL = absoluteString;
|
||||
result = [[QCloudMediaPath alloc] initWithStrippedURL:strippedURL];
|
||||
result.type = QCLOUD_UNIVERSAL_PATH_TYPE_MEDIA;
|
||||
} else if ([absoluteString isBundlePath]) {
|
||||
NSRange range = [absoluteString rangeOfString:kBundlePath];
|
||||
strippedURL = [absoluteString substringFromIndex:range.location + range.length];
|
||||
result = [[QCloudBundlePath alloc] initWithStrippedURL:strippedURL];
|
||||
result.type = QCLOUD_UNIVERSAL_PATH_TYPE_BUNDLE;
|
||||
} else if ([absoluteString isSandboxPath]) {
|
||||
// sandbox
|
||||
NSRange range = [absoluteString rangeOfString:QCloudApplicationDirectory()];
|
||||
strippedURL = [absoluteString substringFromIndex:range.location + range.length];
|
||||
result = [[QCloudSandboxPath alloc] initWithStrippedURL:strippedURL];
|
||||
result.type = QCLOUD_UNIVERSAL_PATH_TYPE_SANDBOX;
|
||||
} else {
|
||||
// Unknown, not stripped
|
||||
strippedURL = absoluteString;
|
||||
result = [[QCloudUniversalFixedPath alloc] initWithStrippedURL:strippedURL];
|
||||
result.type = QCLOUD_UNIVERSAL_PATH_TYPE_FIXED;
|
||||
}
|
||||
QCloudLogDebugP(@"Utils",@"Origin URL is %@ , stripped URL is %@", absoluteString, strippedURL);
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
48
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudFileUtils.h
generated
Normal file
48
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudFileUtils.h
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// QCloudFileUtils.h
|
||||
// Pods
|
||||
//
|
||||
// Created by stonedong on 16/3/6.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudSHAPart.h"
|
||||
#ifndef __QCloudFileUtils
|
||||
#define __QCloudFileUtils
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
|
||||
#define NSShareFileManager [NSFileManager defaultManager]
|
||||
FOUNDATION_EXTERN void QCloudEnsurePathExist(NSString *path);
|
||||
FOUNDATION_EXTERN NSString *QCloudDocumentsPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudDocumentsSubPath(NSString *name);
|
||||
FOUNDATION_EXTERN NSString *QCloudSettingsFilePath();
|
||||
FOUNDATION_EXTERN NSString *QCloudAppendPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudMKTempDirectory();
|
||||
FOUNDATION_EXTERN NSString *QCloudPathJoin(NSString *a, NSString *b);
|
||||
FOUNDATION_EXTERN NSString *QCloudTempDir();
|
||||
FOUNDATION_EXTERN NSString *QCloudTempFilePathWithExtension(NSString *extension);
|
||||
FOUNDATION_EXTERN NSString *QCloudCacheDir();
|
||||
FOUNDATION_EXTERN void QCloudRemoveFileByPath(NSString *path);
|
||||
FOUNDATION_EXTERN NSString *QCloudFileInSubPath(NSString *subPath, NSString *fileName);
|
||||
FOUNDATION_EXTERN BOOL QCloudFileExist(NSString *path);
|
||||
FOUNDATION_EXTERN BOOL QCloudMoveFile(NSString *originPath, NSString *aimPath, NSError *__autoreleasing *error);
|
||||
|
||||
FOUNDATION_EXTERN NSString *QCloudDocumentsTempFilePathWithExcentsion(NSString *extension);
|
||||
FOUNDATION_EXTERN NSString *QCloudApplicationDocumentsPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudApplicationLibaryPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudApplicationTempPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudApplicationDirectory();
|
||||
FOUNDATION_EXTERN NSString *QCloudFilteLocalPath(NSString *originPath);
|
||||
FOUNDATION_EXTERN NSString *QCloudGenerateLocalPath(NSString *pathCompents);
|
||||
FOUNDATION_EXTERN NSURL *QCloudMediaURL(NSString *path);
|
||||
FOUNDATION_EXTERN NSString *QCloudDocumentsTempPath();
|
||||
FOUNDATION_EXTERN NSString *QCloudDocumentsTempFile(NSString *fileName, NSString *extension);
|
||||
FOUNDATION_EXTERN uint64_t QCloudFileSize(NSString *path);
|
||||
FOUNDATION_EXTERN uint64_t QCloudDirectorySize(NSString *path, NSFileManager *fileManager);
|
||||
FOUNDATION_EXTERN NSArray<QCloudSHAPart *> *QCloudIncreaseFileSHAData(NSString *path, uint64_t sliceSize);
|
||||
|
||||
FOUNDATION_EXTERN NSString *detemineFileMemeType(NSURL *filePathURL, NSString *fileName);
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
1020
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudFileUtils.m
generated
Normal file
1020
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudFileUtils.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudModel.h
generated
Normal file
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudModel.h
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// QCloudModel.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/8.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudModel : NSObject
|
||||
|
||||
@end
|
||||
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudModel.m
generated
Normal file
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// QCloudModel.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/8.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudModel.h"
|
||||
|
||||
@implementation QCloudModel
|
||||
|
||||
@end
|
||||
26
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudSHAPart.h
generated
Normal file
26
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudSHAPart.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// QCloudSHAPart.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/8.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudModel.h"
|
||||
@interface QCloudSHAPart : QCloudModel
|
||||
|
||||
/**
|
||||
sha值
|
||||
*/
|
||||
@property (nonatomic, strong) NSString *datasha;
|
||||
|
||||
/**
|
||||
offset
|
||||
*/
|
||||
@property (nonatomic, assign) uint64_t offset;
|
||||
|
||||
/**
|
||||
长度
|
||||
*/
|
||||
@property (nonatomic, assign) uint64_t datalen;
|
||||
@end
|
||||
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudSHAPart.m
generated
Normal file
13
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDFileUtils/QCloudSHAPart.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// QCloudSHAPart.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/8.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudSHAPart.h"
|
||||
|
||||
@implementation QCloudSHAPart
|
||||
|
||||
@end
|
||||
320
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModel.h
generated
Normal file
320
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModel.h
generated
Normal file
@@ -0,0 +1,320 @@
|
||||
//
|
||||
// NSObject+QCloudModel.h
|
||||
// QCloudModel <https://github.com/ibireme/QCloudModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/10.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Provide some data-model method:
|
||||
|
||||
* Convert json to any object, or convert any object to json.
|
||||
* Set object properties with a key-value dictionary (like KVC).
|
||||
* Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`.
|
||||
|
||||
See `QCloudModel` protocol for custom methods.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@interface NSObject (QCloudModel)
|
||||
|
||||
/**
|
||||
Creates and returns a new instance of the receiver from a json.
|
||||
This method is thread-safe.
|
||||
|
||||
@param json A json object in `NSDictionary`, `NSString` or `NSData`.
|
||||
|
||||
@return A new instance created from the json, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)qcloud_modelWithJSON:(id)json;
|
||||
|
||||
/**
|
||||
Creates and returns a new instance of the receiver from a key-value dictionary.
|
||||
This method is thread-safe.
|
||||
|
||||
@param dictionary A key-value dictionary mapped to the instance's properties.
|
||||
Any invalid key-value pair in dictionary will be ignored.
|
||||
|
||||
@return A new instance created from the dictionary, or nil if an error occurs.
|
||||
|
||||
@discussion The key in `dictionary` will mapped to the reciever's property name,
|
||||
and the value will set to the property. If the value's type does not match the
|
||||
property, this method will try to convert the value based on these rules:
|
||||
|
||||
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
|
||||
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
|
||||
`NSString` -> NSURL.
|
||||
`NSValue` -> struct or union, such as CGRect, CGSize, ...
|
||||
`NSString` -> SEL, Class.
|
||||
*/
|
||||
+ (nullable instancetype)qcloud_modelWithDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
/**
|
||||
Set the receiver's properties with a json object.
|
||||
|
||||
@discussion Any invalid data in json will be ignored.
|
||||
|
||||
@param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
|
||||
receiver's properties.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)qcloud_modelSetWithJSON:(id)json;
|
||||
|
||||
/**
|
||||
Set the receiver's properties with a key-value dictionary.
|
||||
|
||||
@param dic A key-value dictionary mapped to the receiver's properties.
|
||||
Any invalid key-value pair in dictionary will be ignored.
|
||||
|
||||
@discussion The key in `dictionary` will mapped to the reciever's property name,
|
||||
and the value will set to the property. If the value's type doesn't match the
|
||||
property, this method will try to convert the value based on these rules:
|
||||
|
||||
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
|
||||
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
|
||||
`NSString` -> NSURL.
|
||||
`NSValue` -> struct or union, such as CGRect, CGSize, ...
|
||||
`NSString` -> SEL, Class.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)qcloud_modelSetWithDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
Generate a json object from the receiver's properties.
|
||||
|
||||
@return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
|
||||
See [NSJSONSerialization isValidJSONObject] for more information.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
|
||||
the inner object to json object.
|
||||
*/
|
||||
- (nullable id)qcloud_modelToJSONObject;
|
||||
|
||||
/**
|
||||
Generate a json string's data from the receiver's properties.
|
||||
|
||||
@return A json string's data, or nil if an error occurs.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
|
||||
inner object to json string.
|
||||
*/
|
||||
- (nullable NSData *)qcloud_modelToJSONData;
|
||||
|
||||
/**
|
||||
Generate a json string from the receiver's properties.
|
||||
|
||||
@return A json string, or nil if an error occurs.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
|
||||
inner object to json string.
|
||||
*/
|
||||
- (nullable NSString *)qcloud_modelToJSONString;
|
||||
|
||||
/**
|
||||
Copy a instance with the receiver's properties.
|
||||
|
||||
@return A copied instance, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable id)qcloud_modelCopy;
|
||||
|
||||
/**
|
||||
Encode the receiver's properties to a coder.
|
||||
|
||||
@param aCoder An archiver object.
|
||||
*/
|
||||
- (void)qcloud_modelEncodeWithCoder:(NSCoder *)aCoder;
|
||||
|
||||
/**
|
||||
Decode the receiver's properties from a decoder.
|
||||
|
||||
@param aDecoder An archiver object.
|
||||
|
||||
@return self
|
||||
*/
|
||||
- (id)qcloud_modelInitWithCoder:(NSCoder *)aDecoder;
|
||||
|
||||
/**
|
||||
Get a hash code with the receiver's properties.
|
||||
|
||||
@return Hash code.
|
||||
*/
|
||||
- (NSUInteger)qcloud_modelHash;
|
||||
|
||||
/**
|
||||
Compares the receiver with another object for equality, based on properties.
|
||||
|
||||
@param model Another object.
|
||||
|
||||
@return `YES` if the reciever is equal to the object, otherwise `NO`.
|
||||
*/
|
||||
- (BOOL)qcloud_modelIsEqual:(id)model;
|
||||
|
||||
/**
|
||||
Description method for debugging purposes based on properties.
|
||||
|
||||
@return A string that describes the contents of the receiver.
|
||||
*/
|
||||
- (NSString *)qcloud_modelDescription;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
Provide some data-model method for NSArray.
|
||||
*/
|
||||
@interface NSArray (QCloudModel)
|
||||
|
||||
/**
|
||||
Creates and returns an array from a json-array.
|
||||
This method is thread-safe.
|
||||
|
||||
@param cls The instance's class in array.
|
||||
@param json A json array of `NSArray`, `NSString` or `NSData`.
|
||||
Example: [{"name","Mary"},{name:"Joe"}]
|
||||
|
||||
@return A array, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSArray *)qcloud_modelArrayWithClass:(Class)cls json:(id)json;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
Provide some data-model method for NSDictionary.
|
||||
*/
|
||||
@interface NSDictionary (QCloudModel)
|
||||
|
||||
/**
|
||||
Creates and returns a dictionary from a json.
|
||||
This method is thread-safe.
|
||||
|
||||
@param cls The value instance's class in dictionary.
|
||||
@param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
|
||||
Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
|
||||
|
||||
@return A dictionary, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSDictionary *)qcloud_modelDictionaryWithClass:(Class)cls json:(id)json;
|
||||
@end
|
||||
|
||||
/**
|
||||
If the default model transform does not fit to your model class, implement one or
|
||||
more method in this protocol to change the default key-value transform process.
|
||||
There's no need to add '<QCloudModel>' to your class header.
|
||||
*/
|
||||
@protocol QCloudModel <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
Custom property mapper.
|
||||
|
||||
@discussion If the key in JSON/Dictionary does not match to the model's property name,
|
||||
implements this method and returns the additional mapper.
|
||||
|
||||
|
||||
@return A custom mapper for properties.
|
||||
*/
|
||||
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
|
||||
|
||||
/**
|
||||
The generic class mapper for container properties.
|
||||
|
||||
@discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
|
||||
implements this method and returns a property->class mapper, tells which kind of
|
||||
object will be add to the array/set/dictionary.
|
||||
|
||||
@return A class mapper.
|
||||
*/
|
||||
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
|
||||
|
||||
/**
|
||||
If you need to create instances of different classes during json->object transform,
|
||||
use the method to choose custom class based on dictionary data.
|
||||
|
||||
@discussion If the model implements this method, it will be called to determine resulting class
|
||||
during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
|
||||
(both singular and containers via `+modelContainerPropertyGenericClass`).
|
||||
|
||||
|
||||
@param dictionary The json/kv dictionary.
|
||||
|
||||
@return Class to create from this dictionary, `nil` to use current class.
|
||||
|
||||
*/
|
||||
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
/**
|
||||
All the properties in blacklist will be ignored in model transform process.
|
||||
Returns nil to ignore this feature.
|
||||
|
||||
@return An array of property's name.
|
||||
*/
|
||||
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
|
||||
|
||||
/**
|
||||
If a property is not in the whitelist, it will be ignored in model transform process.
|
||||
Returns nil to ignore this feature.
|
||||
|
||||
@return An array of property's name.
|
||||
*/
|
||||
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
|
||||
|
||||
/**
|
||||
This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
|
||||
but be called before the model transform.
|
||||
|
||||
@discussion If the model implements this method, it will be called before
|
||||
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
|
||||
If this method returns nil, the transform process will ignore this model.
|
||||
|
||||
@param dic The json/kv dictionary.
|
||||
|
||||
@return Returns the modified dictionary, or nil to ignore this model.
|
||||
*/
|
||||
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
If the default json-to-model transform does not fit to your model object, implement
|
||||
this method to do additional process. You can also use this method to validate the
|
||||
model's properties.
|
||||
|
||||
@discussion If the model implements this method, it will be called at the end of
|
||||
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
|
||||
If this method returns NO, the transform process will ignore this model.
|
||||
|
||||
@param dic The json/kv dictionary.
|
||||
|
||||
@return Returns YES if the model is valid, or NO to ignore this model.
|
||||
*/
|
||||
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
If the default model-to-json transform does not fit to your model class, implement
|
||||
this method to do additional process. You can also use this method to validate the
|
||||
json dictionary.
|
||||
|
||||
@discussion If the model implements this method, it will be called at the end of
|
||||
`-modelToJSONObject` and `-modelToJSONString`.
|
||||
If this method returns NO, the transform process will ignore this json dictionary.
|
||||
|
||||
@param dic The json dictionary.
|
||||
|
||||
@return Returns YES if the model is valid, or NO to ignore this model.
|
||||
*/
|
||||
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
2002
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModel.m
generated
Normal file
2002
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModel.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModelTool.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModelTool.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// NSObject+QCloudModelTool.h
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by karisli(李雪) on 2021/8/2.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSObject (QCloudModelTool)
|
||||
+ (NSArray *)jsonsToModelsWithJsons:(NSArray *)jsons;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
21
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModelTool.m
generated
Normal file
21
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/NSObject+QCloudModelTool.m
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// NSObject+QCloudModelTool.m
|
||||
// QCloudCore
|
||||
//
|
||||
// Created by karisli(李雪) on 2021/8/2.
|
||||
//
|
||||
|
||||
#import "NSObject+QCloudModelTool.h"
|
||||
#import "NSObject+QCloudModel.h"
|
||||
@implementation NSObject (QCloudModelTool)
|
||||
+ (NSArray *)jsonsToModelsWithJsons:(NSArray *)jsons {
|
||||
NSMutableArray *models = [NSMutableArray array];
|
||||
for (NSDictionary *json in jsons) {
|
||||
id model = [[self class] qcloud_modelWithDictionary:json];
|
||||
if (model) {
|
||||
[models addObject:model];
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
@end
|
||||
196
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudClassInfo.h
generated
Normal file
196
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudClassInfo.h
generated
Normal file
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// QCloudClassInfo.h
|
||||
// QCloudModel <https://github.com/ibireme/QCloudModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Type encoding's type.
|
||||
*/
|
||||
typedef NS_OPTIONS(NSUInteger, QCloudEncodingType) {
|
||||
QCloudEncodingTypeMask = 0xFF, ///< mask of type value
|
||||
QCloudEncodingTypeUnknown = 0, ///< unknown
|
||||
QCloudEncodingTypeVoid = 1, ///< void
|
||||
QCloudEncodingTypeBool = 2, ///< bool
|
||||
QCloudEncodingTypeInt8 = 3, ///< char / BOOL
|
||||
QCloudEncodingTypeUInt8 = 4, ///< unsigned char
|
||||
QCloudEncodingTypeInt16 = 5, ///< short
|
||||
QCloudEncodingTypeUInt16 = 6, ///< unsigned short
|
||||
QCloudEncodingTypeInt32 = 7, ///< int
|
||||
QCloudEncodingTypeUInt32 = 8, ///< unsigned int
|
||||
QCloudEncodingTypeInt64 = 9, ///< long long
|
||||
QCloudEncodingTypeUInt64 = 10, ///< unsigned long long
|
||||
QCloudEncodingTypeFloat = 11, ///< float
|
||||
QCloudEncodingTypeDouble = 12, ///< double
|
||||
QCloudEncodingTypeLongDouble = 13, ///< long double
|
||||
QCloudEncodingTypeObject = 14, ///< id
|
||||
QCloudEncodingTypeClass = 15, ///< Class
|
||||
QCloudEncodingTypeSEL = 16, ///< SEL
|
||||
QCloudEncodingTypeBlock = 17, ///< block
|
||||
QCloudEncodingTypePointer = 18, ///< void*
|
||||
QCloudEncodingTypeStruct = 19, ///< struct
|
||||
QCloudEncodingTypeUnion = 20, ///< union
|
||||
QCloudEncodingTypeCString = 21, ///< char*
|
||||
QCloudEncodingTypeCArray = 22, ///< char[10] (for example)
|
||||
|
||||
QCloudEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier
|
||||
QCloudEncodingTypeQualifierConst = 1 << 8, ///< const
|
||||
QCloudEncodingTypeQualifierIn = 1 << 9, ///< in
|
||||
QCloudEncodingTypeQualifierInout = 1 << 10, ///< inout
|
||||
QCloudEncodingTypeQualifierOut = 1 << 11, ///< out
|
||||
QCloudEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
|
||||
QCloudEncodingTypeQualifierByref = 1 << 13, ///< byref
|
||||
QCloudEncodingTypeQualifierOneway = 1 << 14, ///< oneway
|
||||
|
||||
QCloudEncodingTypePropertyMask = 0xFF0000, ///< mask of property
|
||||
QCloudEncodingTypePropertyReadonly = 1 << 16, ///< readonly
|
||||
QCloudEncodingTypePropertyCopy = 1 << 17, ///< copy
|
||||
QCloudEncodingTypePropertyRetain = 1 << 18, ///< retain
|
||||
QCloudEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
|
||||
QCloudEncodingTypePropertyWeak = 1 << 20, ///< weak
|
||||
QCloudEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
|
||||
QCloudEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
|
||||
QCloudEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
|
||||
};
|
||||
|
||||
/**
|
||||
Get the type from a Type-Encoding string.
|
||||
|
||||
@discussion See also:
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
|
||||
|
||||
@param typeEncoding A Type-Encoding string.
|
||||
@return The encoding type.
|
||||
*/
|
||||
QCloudEncodingType QCloudEncodingGetType(const char *typeEncoding);
|
||||
|
||||
/**
|
||||
Instance variable information.
|
||||
*/
|
||||
@interface QCloudClassIvarInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
|
||||
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
|
||||
@property (nonatomic, assign, readonly) QCloudEncodingType type; ///< Ivar's type
|
||||
|
||||
/**
|
||||
Creates and returns an ivar info object.
|
||||
|
||||
@param ivar ivar opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithIvar:(Ivar)ivar;
|
||||
@end
|
||||
|
||||
/**
|
||||
Method information.
|
||||
*/
|
||||
@interface QCloudClassMethodInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< method name
|
||||
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector
|
||||
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types
|
||||
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type
|
||||
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
|
||||
|
||||
/**
|
||||
Creates and returns a method info object.
|
||||
|
||||
@param method method opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithMethod:(Method)method;
|
||||
@end
|
||||
|
||||
/**
|
||||
Property information.
|
||||
*/
|
||||
@interface QCloudClassPropertyInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) objc_property_t property ; ///< property's opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
|
||||
@property (nonatomic, assign, readonly) QCloudEncodingType type; ///< property's type
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
|
||||
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
|
||||
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
|
||||
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
|
||||
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
|
||||
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
|
||||
|
||||
/**
|
||||
Creates and returns a property info object.
|
||||
|
||||
@param property property opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithProperty:(objc_property_t)property;
|
||||
@end
|
||||
|
||||
/**
|
||||
Class information for a class.
|
||||
*/
|
||||
@interface QCloudClassInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Class cls; ///< class object
|
||||
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
|
||||
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
|
||||
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< class name
|
||||
@property (nullable, nonatomic, strong, readonly) QCloudClassInfo *superClassInfo; ///< super class's class info
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, QCloudClassIvarInfo *> *ivarInfos; ///< ivars
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, QCloudClassMethodInfo *> *methodInfos; ///< methods
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, QCloudClassPropertyInfo *> *propertyInfos; ///< properties
|
||||
|
||||
/**
|
||||
If the class is changed (for example: you add a method to this class with
|
||||
'class_addMethod()'), you should call this method to refresh the class info cache.
|
||||
|
||||
After called this method, `needUpdate` will returns `YES`, and you should call
|
||||
'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
|
||||
*/
|
||||
- (void)setNeedUpdate;
|
||||
|
||||
/**
|
||||
If this method returns `YES`, you should stop using this instance and call
|
||||
`classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
|
||||
|
||||
@return Whether this class info need update.
|
||||
*/
|
||||
- (BOOL)needUpdate;
|
||||
|
||||
/**
|
||||
Get the class info of a specified Class.
|
||||
|
||||
@discussion This method will cache the class info and super-class info
|
||||
at the first access to the Class. This method is thread-safe.
|
||||
|
||||
@param cls A class.
|
||||
@return A class info, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)classInfoWithClass:(Class)cls;
|
||||
|
||||
/**
|
||||
Get the class info of a specified Class.
|
||||
|
||||
@discussion This method will cache the class info and super-class info
|
||||
at the first access to the Class. This method is thread-safe.
|
||||
|
||||
@param className A class name.
|
||||
@return A class info, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
407
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudClassInfo.m
generated
Normal file
407
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudClassInfo.m
generated
Normal file
@@ -0,0 +1,407 @@
|
||||
//
|
||||
// QCloudClassInfo.m
|
||||
// QCloudModel <https://github.com/ibireme/QCloudModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "QCloudClassInfo.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
QCloudEncodingType QCloudEncodingGetType(const char *typeEncoding) {
|
||||
char *type = (char *)typeEncoding;
|
||||
if (!type)
|
||||
return QCloudEncodingTypeUnknown;
|
||||
size_t len = strlen(type);
|
||||
if (len == 0)
|
||||
return QCloudEncodingTypeUnknown;
|
||||
|
||||
QCloudEncodingType qualifier = 0;
|
||||
bool prefix = true;
|
||||
while (prefix) {
|
||||
switch (*type) {
|
||||
case 'r': {
|
||||
qualifier |= QCloudEncodingTypeQualifierConst;
|
||||
type++;
|
||||
} break;
|
||||
case 'n': {
|
||||
qualifier |= QCloudEncodingTypeQualifierIn;
|
||||
type++;
|
||||
} break;
|
||||
case 'N': {
|
||||
qualifier |= QCloudEncodingTypeQualifierInout;
|
||||
type++;
|
||||
} break;
|
||||
case 'o': {
|
||||
qualifier |= QCloudEncodingTypeQualifierOut;
|
||||
type++;
|
||||
} break;
|
||||
case 'O': {
|
||||
qualifier |= QCloudEncodingTypeQualifierBycopy;
|
||||
type++;
|
||||
} break;
|
||||
case 'R': {
|
||||
qualifier |= QCloudEncodingTypeQualifierByref;
|
||||
type++;
|
||||
} break;
|
||||
case 'V': {
|
||||
qualifier |= QCloudEncodingTypeQualifierOneway;
|
||||
type++;
|
||||
} break;
|
||||
default: {
|
||||
prefix = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
len = strlen(type);
|
||||
if (len == 0)
|
||||
return QCloudEncodingTypeUnknown | qualifier;
|
||||
|
||||
switch (*type) {
|
||||
case 'v':
|
||||
return QCloudEncodingTypeVoid | qualifier;
|
||||
case 'B':
|
||||
return QCloudEncodingTypeBool | qualifier;
|
||||
case 'c':
|
||||
return QCloudEncodingTypeInt8 | qualifier;
|
||||
case 'C':
|
||||
return QCloudEncodingTypeUInt8 | qualifier;
|
||||
case 's':
|
||||
return QCloudEncodingTypeInt16 | qualifier;
|
||||
case 'S':
|
||||
return QCloudEncodingTypeUInt16 | qualifier;
|
||||
case 'i':
|
||||
return QCloudEncodingTypeInt32 | qualifier;
|
||||
case 'I':
|
||||
return QCloudEncodingTypeUInt32 | qualifier;
|
||||
case 'l':
|
||||
return QCloudEncodingTypeInt32 | qualifier;
|
||||
case 'L':
|
||||
return QCloudEncodingTypeUInt32 | qualifier;
|
||||
case 'q':
|
||||
return QCloudEncodingTypeInt64 | qualifier;
|
||||
case 'Q':
|
||||
return QCloudEncodingTypeUInt64 | qualifier;
|
||||
case 'f':
|
||||
return QCloudEncodingTypeFloat | qualifier;
|
||||
case 'd':
|
||||
return QCloudEncodingTypeDouble | qualifier;
|
||||
case 'D':
|
||||
return QCloudEncodingTypeLongDouble | qualifier;
|
||||
case '#':
|
||||
return QCloudEncodingTypeClass | qualifier;
|
||||
case ':':
|
||||
return QCloudEncodingTypeSEL | qualifier;
|
||||
case '*':
|
||||
return QCloudEncodingTypeCString | qualifier;
|
||||
case '^':
|
||||
return QCloudEncodingTypePointer | qualifier;
|
||||
case '[':
|
||||
return QCloudEncodingTypeCArray | qualifier;
|
||||
case '(':
|
||||
return QCloudEncodingTypeUnion | qualifier;
|
||||
case '{':
|
||||
return QCloudEncodingTypeStruct | qualifier;
|
||||
case '@': {
|
||||
if (len == 2 && *(type + 1) == '?')
|
||||
return QCloudEncodingTypeBlock | qualifier;
|
||||
else
|
||||
return QCloudEncodingTypeObject | qualifier;
|
||||
}
|
||||
default:
|
||||
return QCloudEncodingTypeUnknown | qualifier;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation QCloudClassIvarInfo
|
||||
|
||||
- (instancetype)initWithIvar:(Ivar)ivar {
|
||||
if (!ivar)
|
||||
return nil;
|
||||
self = [super init];
|
||||
_ivar = ivar;
|
||||
const char *name = ivar_getName(ivar);
|
||||
if (name) {
|
||||
_name = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
_offset = ivar_getOffset(ivar);
|
||||
const char *typeEncoding = ivar_getTypeEncoding(ivar);
|
||||
if (typeEncoding) {
|
||||
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
|
||||
_type = QCloudEncodingGetType(typeEncoding);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudClassMethodInfo
|
||||
|
||||
- (instancetype)initWithMethod:(Method)method {
|
||||
if (!method)
|
||||
return nil;
|
||||
self = [super init];
|
||||
_method = method;
|
||||
_sel = method_getName(method);
|
||||
_imp = method_getImplementation(method);
|
||||
const char *name = sel_getName(_sel);
|
||||
if (name) {
|
||||
_name = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
const char *typeEncoding = method_getTypeEncoding(method);
|
||||
if (typeEncoding) {
|
||||
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
|
||||
}
|
||||
char *returnType = method_copyReturnType(method);
|
||||
if (returnType) {
|
||||
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
|
||||
free(returnType);
|
||||
}
|
||||
unsigned int argumentCount = method_getNumberOfArguments(method);
|
||||
if (argumentCount > 0) {
|
||||
NSMutableArray *argumentTypes = [NSMutableArray new];
|
||||
for (unsigned int i = 0; i < argumentCount; i++) {
|
||||
char *argumentType = method_copyArgumentType(method, i);
|
||||
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
|
||||
[argumentTypes addObject:type ? type : @""];
|
||||
if (argumentType)
|
||||
free(argumentType);
|
||||
}
|
||||
_argumentTypeEncodings = argumentTypes;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudClassPropertyInfo
|
||||
|
||||
- (instancetype)initWithProperty:(objc_property_t)property {
|
||||
if (!property)
|
||||
return nil;
|
||||
self = [super init];
|
||||
_property = property;
|
||||
const char *name = property_getName(property);
|
||||
if (name) {
|
||||
_name = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
|
||||
QCloudEncodingType type = 0;
|
||||
unsigned int attrCount;
|
||||
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
|
||||
for (unsigned int i = 0; i < attrCount; i++) {
|
||||
switch (attrs[i].name[0]) {
|
||||
case 'T': { // Type encoding
|
||||
if (attrs[i].value) {
|
||||
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
|
||||
type = QCloudEncodingGetType(attrs[i].value);
|
||||
|
||||
if ((type & QCloudEncodingTypeMask) == QCloudEncodingTypeObject && _typeEncoding.length) {
|
||||
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
|
||||
if (![scanner scanString:@"@\"" intoString:NULL])
|
||||
continue;
|
||||
|
||||
NSString *clsName = nil;
|
||||
if ([scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
|
||||
if (clsName.length)
|
||||
_cls = objc_getClass(clsName.UTF8String);
|
||||
}
|
||||
|
||||
NSMutableArray *protocols = nil;
|
||||
while ([scanner scanString:@"<" intoString:NULL]) {
|
||||
NSString *protocol = nil;
|
||||
if ([scanner scanUpToString:@">" intoString:&protocol]) {
|
||||
if (protocol.length) {
|
||||
if (!protocols)
|
||||
protocols = [NSMutableArray new];
|
||||
[protocols addObject:protocol];
|
||||
}
|
||||
}
|
||||
[scanner scanString:@">" intoString:NULL];
|
||||
}
|
||||
_protocols = protocols;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 'V': { // Instance variable
|
||||
if (attrs[i].value) {
|
||||
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
|
||||
}
|
||||
} break;
|
||||
case 'R': {
|
||||
type |= QCloudEncodingTypePropertyReadonly;
|
||||
} break;
|
||||
case 'C': {
|
||||
type |= QCloudEncodingTypePropertyCopy;
|
||||
} break;
|
||||
case '&': {
|
||||
type |= QCloudEncodingTypePropertyRetain;
|
||||
} break;
|
||||
case 'N': {
|
||||
type |= QCloudEncodingTypePropertyNonatomic;
|
||||
} break;
|
||||
case 'D': {
|
||||
type |= QCloudEncodingTypePropertyDynamic;
|
||||
} break;
|
||||
case 'W': {
|
||||
type |= QCloudEncodingTypePropertyWeak;
|
||||
} break;
|
||||
case 'G': {
|
||||
type |= QCloudEncodingTypePropertyCustomGetter;
|
||||
if (attrs[i].value) {
|
||||
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
|
||||
}
|
||||
} break;
|
||||
case 'S': {
|
||||
type |= QCloudEncodingTypePropertyCustomSetter;
|
||||
if (attrs[i].value) {
|
||||
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
|
||||
}
|
||||
} // break; commented for code coverage in next line
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attrs) {
|
||||
free(attrs);
|
||||
attrs = NULL;
|
||||
}
|
||||
|
||||
_type = type;
|
||||
if (_name.length) {
|
||||
if (!_getter) {
|
||||
_getter = NSSelectorFromString(_name);
|
||||
}
|
||||
if (!_setter && _name.length) {
|
||||
_setter = NSSelectorFromString(
|
||||
[NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudClassInfo {
|
||||
BOOL _needUpdate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClass:(Class)cls {
|
||||
if (!cls)
|
||||
return nil;
|
||||
self = [super init];
|
||||
_cls = cls;
|
||||
_superCls = class_getSuperclass(cls);
|
||||
_isMeta = class_isMetaClass(cls);
|
||||
if (!_isMeta) {
|
||||
_metaCls = objc_getMetaClass(class_getName(cls));
|
||||
}
|
||||
_name = NSStringFromClass(cls);
|
||||
[self _update];
|
||||
|
||||
_superClassInfo = [self.class classInfoWithClass:_superCls];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_update {
|
||||
_ivarInfos = nil;
|
||||
_methodInfos = nil;
|
||||
_propertyInfos = nil;
|
||||
|
||||
Class cls = self.cls;
|
||||
unsigned int methodCount = 0;
|
||||
Method *methods = class_copyMethodList(cls, &methodCount);
|
||||
if (methods) {
|
||||
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
|
||||
_methodInfos = methodInfos;
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
QCloudClassMethodInfo *info = [[QCloudClassMethodInfo alloc] initWithMethod:methods[i]];
|
||||
if (info.name)
|
||||
methodInfos[info.name] = info;
|
||||
}
|
||||
free(methods);
|
||||
}
|
||||
unsigned int propertyCount = 0;
|
||||
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
|
||||
if (properties) {
|
||||
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
|
||||
_propertyInfos = propertyInfos;
|
||||
for (unsigned int i = 0; i < propertyCount; i++) {
|
||||
QCloudClassPropertyInfo *info = [[QCloudClassPropertyInfo alloc] initWithProperty:properties[i]];
|
||||
if (info.name)
|
||||
propertyInfos[info.name] = info;
|
||||
}
|
||||
free(properties);
|
||||
}
|
||||
|
||||
unsigned int ivarCount = 0;
|
||||
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
|
||||
if (ivars) {
|
||||
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
|
||||
_ivarInfos = ivarInfos;
|
||||
for (unsigned int i = 0; i < ivarCount; i++) {
|
||||
QCloudClassIvarInfo *info = [[QCloudClassIvarInfo alloc] initWithIvar:ivars[i]];
|
||||
if (info.name)
|
||||
ivarInfos[info.name] = info;
|
||||
}
|
||||
free(ivars);
|
||||
}
|
||||
|
||||
if (!_ivarInfos)
|
||||
_ivarInfos = @{};
|
||||
if (!_methodInfos)
|
||||
_methodInfos = @{};
|
||||
if (!_propertyInfos)
|
||||
_propertyInfos = @{};
|
||||
|
||||
_needUpdate = NO;
|
||||
}
|
||||
|
||||
- (void)setNeedUpdate {
|
||||
_needUpdate = YES;
|
||||
}
|
||||
|
||||
- (BOOL)needUpdate {
|
||||
return _needUpdate;
|
||||
}
|
||||
|
||||
+ (instancetype)classInfoWithClass:(Class)cls {
|
||||
if (!cls)
|
||||
return nil;
|
||||
static CFMutableDictionaryRef classCache;
|
||||
static CFMutableDictionaryRef metaCache;
|
||||
static dispatch_once_t onceToken;
|
||||
static dispatch_semaphore_t lock;
|
||||
dispatch_once(&onceToken, ^{
|
||||
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
lock = dispatch_semaphore_create(1);
|
||||
});
|
||||
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
|
||||
QCloudClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
|
||||
if (info && info->_needUpdate) {
|
||||
[info _update];
|
||||
}
|
||||
dispatch_semaphore_signal(lock);
|
||||
if (!info) {
|
||||
info = [[QCloudClassInfo alloc] initWithClass:cls];
|
||||
if (info) {
|
||||
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
|
||||
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
|
||||
dispatch_semaphore_signal(lock);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (instancetype)classInfoWithClassName:(NSString *)className {
|
||||
Class cls = NSClassFromString(className);
|
||||
return [self classInfoWithClass:cls];
|
||||
}
|
||||
|
||||
@end
|
||||
22
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudObjectModel.h
generated
Executable file
22
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDObjectModel/QCloudObjectModel.h
generated
Executable file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// QCloudModel.h
|
||||
// QCloudModel <https://github.com/ibireme/QCloudModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/10.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<QCloudCore/QCloudCore.h>)
|
||||
FOUNDATION_EXPORT double QCloudModelVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char QCloudModelVersionString[];
|
||||
#import <QCloudCore/NSObject+QCloudModel.h>
|
||||
#import <QCloudCore/QCloudClassInfo.h>
|
||||
#else
|
||||
#import "NSObject+QCloudModel.h"
|
||||
#import "QCloudClassInfo.h"
|
||||
#endif
|
||||
98
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDProgramDefines/QCloudProgrameDefines.h
generated
Normal file
98
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDProgramDefines/QCloudProgrameDefines.h
generated
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// DZProgrameDefines.h
|
||||
// TimeUI
|
||||
//
|
||||
// Created by Stone Dong on 14-1-21.
|
||||
// Copyright (c) 2014年 Stone Dong. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
|
||||
#define DEFINE_PROPERTY_KEY(key) static void const kPK##key = &kPK##key
|
||||
|
||||
/**
|
||||
* 定义字符串
|
||||
*/
|
||||
#define DEFINE_NSString(str) static NSString *const kDZ##str = @"" #str;
|
||||
|
||||
#define DEFINE_NSStringValue(str, value) static NSString *const kDZ##str = @"" #value;
|
||||
|
||||
#define DEFINE_NOTIFICATION_MESSAGE(str) static NSString *const kDZNotification_##str = @"" #str;
|
||||
|
||||
#define DEFINE_PROPERTY_READONLY(mnmKind, type, name) @property (nonatomic, mnmKind, readonly) type name
|
||||
|
||||
#define DEFINE_PROPERTY(mnmKind, type, name) @property (nonatomic, mnmKind) type name
|
||||
#define DEFINE_PROPERTY_ASSIGN(type, name) DEFINE_PROPERTY(assign, type, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_Float(name) DEFINE_PROPERTY_ASSIGN(float, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_INT64(name) DEFINE_PROPERTY_ASSIGN(int64_t, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_INT32(name) DEFINE_PROPERTY_ASSIGN(int32_t, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_INT16(name) DEFINE_PROPERTY_ASSIGN(int16_t, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_INT8(name) DEFINE_PROPERTY_ASSIGN(int8_t, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_Double(name) DEFINE_PROPERTY_ASSIGN(double, name)
|
||||
#define DEFINE_PROPERTY_ASSIGN_BOOL(name) DEFINE_PROPERTY_ASSIGN(BOOL, name)
|
||||
|
||||
#define DEFINE_PROPERTY_STRONG_READONLY(type, name) DEFINE_PROPERTY_READONLY(strong, type, name)
|
||||
|
||||
#define DEFINE_PROPERTY_STRONG(type, name) DEFINE_PROPERTY(strong, type, name)
|
||||
#define DEFINE_PROPERTY_STRONG_UILabel(name) DEFINE_PROPERTY_STRONG(UILabel *, name)
|
||||
#define DEFINE_PROPERTY_STRONG_NSString(name) DEFINE_PROPERTY_STRONG(NSString *, name)
|
||||
|
||||
#define DEFINE_PROPERTY_STRONG_NSDate(name) DEFINE_PROPERTY_STRONG(NSDate *, name)
|
||||
#define DEFINE_PROPERTY_STRONG_NSArray(name) DEFINE_PROPERTY_STRONG(NSArray *, name)
|
||||
#define DEFINE_PROPERTY_STRONG_UIImageView(name) DEFINE_PROPERTY_STRONG(UIImageView *, name)
|
||||
#define DEFINE_PROPERTY_STRONG_UIButton(name) DEFINE_PROPERTY_STRONG(UIButton *, name)
|
||||
|
||||
#define DEFINE_PROPERTY_WEAK(type, name) DEFINE_PROPERTY(weak, type, name)
|
||||
|
||||
#define DEFINE_DZ_EXTERN_STRING(key) extern NSString *const key;
|
||||
#define INIT_DZ_EXTERN_STRING(key, value) NSString *const key = @"" #value;
|
||||
|
||||
#define DZ_CheckObjcetClass(object, cla) [object isKindOfClass:[cla class]]
|
||||
|
||||
/**
|
||||
数据类型的转化
|
||||
*/
|
||||
|
||||
#define DZ_STR_2_URL(str) (([str hasPrefix:@"http"] || !str) ? [NSURL URLWithString:str] : [NSURL fileURLWithPath:str])
|
||||
#define DZ_NUM_2_STR(num) [@(num) stringValue]
|
||||
|
||||
// Notification defaults
|
||||
|
||||
#define DZExternObserverMessage(msg) \
|
||||
void DZAddObserverFor##msg(NSObject *ob, SEL selector); \
|
||||
void DZRemoveObserverFor##msg(NSObject *ob); \
|
||||
void DZPost##msg(NSDictionary *dic);
|
||||
|
||||
#define DZObserverMessage(message) \
|
||||
void DZAddObserverFor##message(NSObject *ob, SEL selector) { \
|
||||
[[NSNotificationCenter defaultCenter] addObserver:ob selector:selector name:@"" #message object:nil]; \
|
||||
} \
|
||||
\
|
||||
void DZRemoveObserverFor##message(NSObject *ob) { \
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:ob name:@"" #message object:nil]; \
|
||||
} \
|
||||
\
|
||||
void DZPost##message(NSDictionary *dic) { \
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"" #message object:nil userInfo:dic]; \
|
||||
}
|
||||
|
||||
FOUNDATION_EXTERN void DZEnsureMainThread(void (^mainSafeBlock)());
|
||||
|
||||
#define DZEnsureMainThreadBegin DZEnsureMainThread(^{
|
||||
#define DZEnsureMainThreadEnd \
|
||||
});
|
||||
|
||||
//定义block中使用的变量
|
||||
|
||||
#define QCloudWeakSelf(type) __weak typeof(type) weak##type = type
|
||||
#define QCloudStrongSelf(type) __strong typeof(weak##type) strong##type = weak##type
|
||||
|
||||
#pragma mark----
|
||||
|
||||
#define bQCloudSystemVersion(min, max) \
|
||||
([UIDevice currentDevice].systemVersion.doubleValue >= min) && ([UIDevice currentDevice].systemVersion.doubleValue <= max)
|
||||
#define bQCloudSystemVersion8 bQCloudSystemVersion(8.0, 8.999)
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
21
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDProgramDefines/QCloudProgrameDefines.m
generated
Normal file
21
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDProgramDefines/QCloudProgrameDefines.m
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// DZProgramDefines.m
|
||||
// TimeUI
|
||||
//
|
||||
// Created by Stone Dong on 14-1-21.
|
||||
// Copyright (c) 2014年 Stone Dong. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudProgrameDefines.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
void DZEnsureMainThread(void (^mainSafeBlock)(void)) {
|
||||
if (mainSafeBlock == NULL) {
|
||||
return;
|
||||
}
|
||||
if ([NSThread isMainThread]) {
|
||||
mainSafeBlock();
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), mainSafeBlock);
|
||||
}
|
||||
}
|
||||
32
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/API/QCloudNetworkingAPI.h
generated
Normal file
32
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/API/QCloudNetworkingAPI.h
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// QCloudNetworkingAPI.h
|
||||
// QCloudTernimalLab_CommonLogic
|
||||
//
|
||||
// Created by tencent on 5/12/16.
|
||||
// Copyright © 2016 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudHTTPRequestDelegate.h"
|
||||
|
||||
extern NSString *const kQCloudRestNetURLUsageNotification;
|
||||
|
||||
@interface NSDictionary (QCloudRestNetUsage)
|
||||
- (NSURL *)bdwt_RestNetCoreUsagedURL;
|
||||
@end
|
||||
|
||||
@class QCloudHTTPRequest;
|
||||
@class QCloudRequestOperation;
|
||||
@protocol QCloudNetworkingAPI <NSObject>
|
||||
/**
|
||||
最大并发的网络线程数量
|
||||
*/
|
||||
@property (atomic, assign) int32_t maxConcurrencyTask;
|
||||
+ (NSObject<QCloudNetworkingAPI> *)shareClient;
|
||||
- (int)performRequest:(QCloudHTTPRequest *)httpRequst;
|
||||
- (int)performRequest:(QCloudHTTPRequest *)httpRequst withFinishBlock:(QCloudRequestFinishBlock)block;
|
||||
- (void)cancelRequestWithID:(int)requestID;
|
||||
- (void)cancelAllRequest;
|
||||
- (void)cancelRequestsWithID:(NSArray<NSNumber *> *)requestIDs;
|
||||
- (void)executeRestHTTPReqeust:(QCloudHTTPRequest *)httpRequest;
|
||||
@end
|
||||
14
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/NSHTTPCookie+QCloudNetworking.h
generated
Normal file
14
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/NSHTTPCookie+QCloudNetworking.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// NSHTTPCookie+QCloudNetworking.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/29.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXTERN NSArray *QCloudFuseAndUpdateCookiesArray(NSArray *source, NSArray *aim);
|
||||
@interface NSHTTPCookie (QCloudNetworking)
|
||||
- (BOOL)isEqualToQCloudCookie:(NSHTTPCookie *)c;
|
||||
@end
|
||||
43
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/NSHTTPCookie+QCloudNetworking.m
generated
Normal file
43
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/NSHTTPCookie+QCloudNetworking.m
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// NSHTTPCookie+QCloudNetworking.m
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/29.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSHTTPCookie+QCloudNetworking.h"
|
||||
|
||||
NSArray *QCloudFuseAndUpdateCookiesArray(NSArray *source, NSArray *aim) {
|
||||
NSMutableArray *aimArray = [NSMutableArray new];
|
||||
if (source.count) {
|
||||
[aimArray addObjectsFromArray:source];
|
||||
}
|
||||
for (NSHTTPCookie *s in source) {
|
||||
if (![aimArray containsObject:s]) {
|
||||
[aimArray addObject:s];
|
||||
}
|
||||
}
|
||||
return aimArray;
|
||||
}
|
||||
|
||||
@implementation NSHTTPCookie (QCloudNetworking)
|
||||
- (BOOL)isEqualToQCloudCookie:(NSHTTPCookie *)c {
|
||||
if (![c.name isEqualToString:self.name]) {
|
||||
return NO;
|
||||
}
|
||||
if (![c.value isEqualToString:self.value]) {
|
||||
return NO;
|
||||
}
|
||||
if (![c.path isEqualToString:self.path]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *maxDomain = c.domain.length > self.domain.length ? c.domain : self.domain;
|
||||
NSString *minDomain = c.domain.length < self.domain.length ? c.domain : self.domain;
|
||||
if ([maxDomain hasSuffix:minDomain]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
134
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudAbstractRequest.h
generated
Normal file
134
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudAbstractRequest.h
generated
Normal file
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// QCloudAbstractRequest.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/10.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudHTTPRequestDelegate.h"
|
||||
#import "QCloudHttpMetrics.h"
|
||||
#import "QCloudCredential.h"
|
||||
#import "QCloudEndPoint.h"
|
||||
#import "QCloudSignature.h"
|
||||
typedef double QCloudAbstractRequestPriority;
|
||||
|
||||
#define QCloudAbstractRequestPriorityHigh 3.0
|
||||
#define QCloudAbstractRequestPriorityNormal 2.0
|
||||
#define QCloudAbstractRequestPriorityLow 1.0
|
||||
#define QCloudAbstractRequestPriorityBackground 0.0
|
||||
typedef void (^QCloudRequestSendProcessBlock)(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
|
||||
typedef void (^QCloudRequestDownProcessBlock)(int64_t bytesDownload, int64_t totalBytesDownload, int64_t totalBytesExpectedToDownload);
|
||||
typedef void (^QCloudRequestDownProcessWithDataBlock)(int64_t bytesDownload, int64_t totalBytesDownload, int64_t totalBytesExpectedToDownload,
|
||||
NSData * _Nullable receivedData);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, QCloudRequestNetworkType) {
|
||||
QCloudRequestNetworkNone = 0,
|
||||
QCloudRequestNetworkHttp,
|
||||
QCloudRequestNetworkQuic,
|
||||
};
|
||||
/**
|
||||
请求的抽象基类,该类封装了用于进行request-response模式数据请求的通用属性和接口。包括发起请求,相应结果,优先级处理,性能监控能常见特性。
|
||||
*/
|
||||
@interface QCloudAbstractRequest : NSObject {
|
||||
@protected
|
||||
int64_t _requestID;
|
||||
}
|
||||
/**
|
||||
签名信息的回调接口,该委托必须实现。签名是腾讯云进行服务时进行用户身份校验的关键手段,同时也保障了用户访问的安全性。该委托中通过函数回调来提供签名信息。
|
||||
*/
|
||||
|
||||
/**
|
||||
指定接口级请求域名
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) QCloudEndPoint * endpoint;
|
||||
|
||||
/// 用于外部指定网络请求使用何种协议。默认QCloudRequestNetworkHttp。优先级高于config.enableQuic。
|
||||
@property (nonatomic, assign)QCloudRequestNetworkType networkType;
|
||||
|
||||
/// sdk 内部使用,外部设置无效。
|
||||
@property (nonatomic, assign) BOOL enableQuic;
|
||||
@property (atomic, assign) BOOL forbidCancelled;
|
||||
@property (atomic, assign) BOOL requestRetry;
|
||||
@property (atomic, assign, readonly) BOOL canceled;
|
||||
@property (nonatomic, assign, readonly) int64_t requestID;
|
||||
@property (nonatomic, assign) QCloudAbstractRequestPriority priority;
|
||||
@property (nonatomic, strong, readonly) QCloudHttpMetrics *_Nullable benchMarkMan;
|
||||
@property (atomic, assign, readonly) BOOL finished;
|
||||
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
|
||||
/**
|
||||
用于业务中携带与request关联的参数,不参与SDK内部逻辑与网络请求;
|
||||
*/
|
||||
@property (nonatomic, strong ,nullable) NSDictionary * payload;
|
||||
|
||||
/**
|
||||
设置接口级参与签头部和参数。
|
||||
|
||||
默认下方所有字段参与签名,无需设置,若指定某个字段不参与签名,则将相应的字段删除,然后将数组赋值给shouldSignedList即可。
|
||||
@[@"Cache-Control", @"Content-Disposition", @"Content-Encoding", @"Content-Length", @"Content-MD5", @"Content-Type", @"Expect", @"Expires", @"If-Match" , @"If-Modified-Since" , @"If-None-Match" , @"If-Unmodified-Since" , @"Origin" , @"Range" , @"transfer-encoding" ,@"Host",@"Pic-Operations",@"ci-process"]
|
||||
|
||||
示例:
|
||||
1、指定Host不参与签名(删除数组中的@"Host")。
|
||||
shouldSignedList = @[@"Cache-Control", @"Content-Disposition", @"Content-Encoding", @"Content-Length", @"Content-MD5", @"Content-Type", @"Expect", @"Expires", @"If-Match" , @"If-Modified-Since" , @"If-None-Match" , @"If-Unmodified-Since" , @"Origin" , @"Range" , @"transfer-encoding" ,@"Pic-Operations",@"ci-process"];
|
||||
|
||||
2、指定所有字段都不参与签名。
|
||||
shouldSignedList = @[];
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) NSArray *shouldSignedList;
|
||||
@property (nonatomic, strong, nullable) QCloudCredential * credential;
|
||||
|
||||
@property (nonatomic, strong, nullable) QCloudSignature * signature;
|
||||
/**
|
||||
协议执行结果向外通知的委托(delegate)主要包括成功和失败两种情况。与Block方式并存,当两者都设置的时候都会通知。
|
||||
*/
|
||||
@property (nonatomic, weak) id<QCloudHTTPRequestDelegate> _Nullable delegate;
|
||||
/**
|
||||
协议执行结果向外通知的Block,与delegate方式并存,当两者都设置的时候都会通知。
|
||||
*/
|
||||
@property (nonatomic, strong) QCloudRequestFinishBlock _Nullable finishBlock;
|
||||
|
||||
@property (nonatomic, strong) QCloudRequestSendProcessBlock _Nullable sendProcessBlock;
|
||||
|
||||
@property (nonatomic, strong) QCloudRequestDownProcessBlock _Nullable downProcessBlock;
|
||||
|
||||
@property (nonatomic, strong) QCloudRequestDownProcessWithDataBlock _Nullable downProcessWithDataBlock;
|
||||
- (void)setFinishBlock:(void (^_Nullable)(id _Nullable outputObject, NSError *_Nullable error))QCloudRequestFinishBlock;
|
||||
- (void)setDownProcessBlock:(void (^_Nullable)(int64_t bytesDownload, int64_t totalBytesDownload,
|
||||
int64_t totalBytesExpectedToDownload))downloadProcessBlock;
|
||||
- (void)setDownProcessWithDataBlock:(void (^_Nullable)(int64_t bytesDownload, int64_t totalBytesDownload,
|
||||
|
||||
int64_t totalBytesExpectedToDownload, NSData * _Nullable receiveData))downloadProcessWithDataBlock;
|
||||
|
||||
- (void)setSendProcessBlock:(void (^_Nullable)(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))sendProcessBlock;
|
||||
|
||||
/**
|
||||
请求过程出错,进行处理。默认只处理HTTP协议层错误信息。并进行delegate的通知。
|
||||
@param error 请求过程出错信息,默认只处理HTTP层错误信息
|
||||
*/
|
||||
- (void)onError:(NSError *_Nonnull)error;
|
||||
|
||||
/**
|
||||
请求过程成功,并获取到了数据,进行处理。并进行delegate的通知。
|
||||
@param object 获取到的数据,经过responseserilizer处理的后的数据。
|
||||
*/
|
||||
- (void)onSuccess:(id _Nonnull)object;
|
||||
|
||||
- (void)notifySuccess:(id _Nonnull)object;
|
||||
- (void)notifyError:(NSError *_Nonnull)error;
|
||||
- (void)notifyDownloadProgressBytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload;
|
||||
|
||||
- (void)notifyDownloadProgressBytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload
|
||||
receivedData:(NSData *_Nullable)data;
|
||||
|
||||
- (void)notifySendProgressBytesSend:(int64_t)bytesSend
|
||||
totalBytesSend:(int64_t)totalBytesSend
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
|
||||
- (void)cancel;
|
||||
- (void)waitForComplete;
|
||||
- (void)configTaskResume;
|
||||
@end
|
||||
240
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudAbstractRequest.m
generated
Normal file
240
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudAbstractRequest.m
generated
Normal file
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// QCloudAbstractRequest.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/3/10.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudAbstractRequest.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "QCloudNetEnv.h"
|
||||
#import "NSError+QCloudNetworking.h"
|
||||
__attribute__((noinline)) void cosWarnBlockingOperationOnMainThread(void) {
|
||||
NSLog(@"Warning: A long-running operation is being executed on the main thread. \n"
|
||||
" Break on warnBlockingOperationOnMainThread() to debug.");
|
||||
}
|
||||
@interface QCloudAbstractRequest ()
|
||||
@property (nonatomic, strong) NSObject *lock;
|
||||
@property (nonatomic, strong) NSCondition *condition;
|
||||
|
||||
@end
|
||||
@implementation QCloudAbstractRequest
|
||||
@synthesize requestID = _requestID;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_lock = [[NSObject alloc] init];
|
||||
_condition = [[NSCondition alloc] init];
|
||||
_benchMarkMan = [QCloudHttpMetrics new];
|
||||
_priority = QCloudAbstractRequestPriorityHigh;
|
||||
static int64_t requestID = 3333;
|
||||
_requestID = (++ requestID) * 1000 + arc4random_uniform(1000);
|
||||
_finished = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)__notifyError:(NSError *)error {
|
||||
[self.condition broadcast];
|
||||
// [self.benchMarkMan benginWithKey:kRNBenchmarkLogicOnly];
|
||||
if ([self.delegate respondsToSelector:@selector(QCloudHTTPRequestDidFinished:failed:)]) {
|
||||
[self.delegate QCloudHTTPRequestDidFinished:self failed:error];
|
||||
}
|
||||
if (self.finishBlock) {
|
||||
self.finishBlock(nil, error);
|
||||
}
|
||||
self.finishBlock = nil;
|
||||
// [self.benchMarkMan markFinishWithKey:kRNBenchmarkLogicOnly];
|
||||
}
|
||||
- (void)notifyError:(NSError *)error {
|
||||
if (![NSThread isMainThread]) {
|
||||
[self __notifyError:error];
|
||||
} else {
|
||||
[self performSelectorInBackground:@selector(__notifyError:) withObject:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onError:(NSError *)error {
|
||||
@synchronized(self) {
|
||||
if (_finished) {
|
||||
return;
|
||||
}
|
||||
_finished = YES;
|
||||
}
|
||||
[self.benchMarkMan markFinishWithKey:kTaskTookTime];
|
||||
[self notifyError:error];
|
||||
if (self.requestRetry) {
|
||||
_finished = NO;
|
||||
}else{
|
||||
_finished = YES;
|
||||
}
|
||||
QCloudLogErrorE(@"",@"[%@][%lld]当前网络环境为%d 请求失败%@", [self class], self.requestID, QCloudNetworkShareEnv.currentNetStatus, error);
|
||||
QCloudLogInfoN(@"",@"[%@][%lld]性能监控 %@", [self class], self.requestID, [self.benchMarkMan readablityDescription]);
|
||||
}
|
||||
|
||||
- (void)__notifySuccess:(id)object {
|
||||
[self.condition broadcast];
|
||||
if ([self.delegate respondsToSelector:@selector(QCloudHTTPRequestDidFinished:succeedWithObject:)]) {
|
||||
[self.delegate QCloudHTTPRequestDidFinished:self succeedWithObject:object];
|
||||
}
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
if (self.finishBlock) {
|
||||
self.finishBlock(object, nil);
|
||||
}
|
||||
self.finishBlock = nil;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)notifySuccess:(id)object {
|
||||
if (![NSThread isMainThread]) {
|
||||
[self __notifySuccess:object];
|
||||
} else {
|
||||
[self performSelectorInBackground:@selector(__notifySuccess:) withObject:object];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSuccess:(id)object {
|
||||
@synchronized(self) {
|
||||
if (_finished) {
|
||||
return;
|
||||
}
|
||||
_finished = YES;
|
||||
}
|
||||
[self.benchMarkMan markFinishWithKey:kTaskTookTime];
|
||||
[self notifySuccess:object];
|
||||
if (self.requestRetry) {
|
||||
_finished = NO;
|
||||
}else{
|
||||
_finished = YES;
|
||||
}
|
||||
QCloudLogInfoN(@"",@"[%@][%lld]性能监控\n%@", [self class], self.requestID, [self.benchMarkMan readablityDescription]);
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
@synchronized(self) {
|
||||
_canceled = YES;
|
||||
}
|
||||
NSError *cancelError = [NSError qcloud_errorWithCode:QCloudNetworkErrorCodeCanceled message:@"UserCancelled:The request is canceled"];
|
||||
[self onError:cancelError];
|
||||
}
|
||||
|
||||
- (void)notifyDownloadProgressBytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload {
|
||||
if (self.canceled) {
|
||||
return;
|
||||
}
|
||||
if (self.downProcessBlock) {
|
||||
self.downProcessBlock(bytesDownload, totalBytesDownload, totalBytesExpectedToDownload);
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(QCloudHTTPRequest:bytesDownload:totalBytesDownload:totalBytesExpectedToDownload:)]) {
|
||||
[self.delegate QCloudHTTPRequest:self
|
||||
bytesDownload:bytesDownload
|
||||
totalBytesDownload:totalBytesDownload
|
||||
totalBytesExpectedToDownload:totalBytesExpectedToDownload];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyDownloadProgressBytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload
|
||||
receivedData:(NSData *)data {
|
||||
[self notifyDownloadProgressBytesDownload:bytesDownload
|
||||
totalBytesDownload:totalBytesDownload
|
||||
totalBytesExpectedToDownload:totalBytesExpectedToDownload];
|
||||
|
||||
if (self.downProcessWithDataBlock) {
|
||||
self.downProcessWithDataBlock(bytesDownload, totalBytesDownload, totalBytesExpectedToDownload, data);
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(QCloudHTTPRequest:bytesDownload:totalBytesDownload:totalBytesExpectedToDownload:receiveData:)]) {
|
||||
[self.delegate QCloudHTTPRequest:self
|
||||
bytesDownload:bytesDownload
|
||||
totalBytesDownload:totalBytesDownload
|
||||
totalBytesExpectedToDownload:totalBytesExpectedToDownload
|
||||
receiveData:data];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifySendProgressBytesSend:(int64_t)bytesSend
|
||||
totalBytesSend:(int64_t)totalBytesSend
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
||||
if (self.canceled) {
|
||||
return;
|
||||
}
|
||||
if (self.sendProcessBlock) {
|
||||
self.sendProcessBlock(bytesSend, totalBytesSend, totalBytesExpectedToSend);
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(QCloudHTTPRequest:sendBytes:totalBytesSent:totalBytesExpectedToSend:)]) {
|
||||
[self.delegate QCloudHTTPRequest:self sendBytes:bytesSend totalBytesSent:totalBytesSend totalBytesExpectedToSend:totalBytesExpectedToSend];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)waitForComplete {
|
||||
if ([NSThread isMainThread]) {
|
||||
cosWarnBlockingOperationOnMainThread();
|
||||
}
|
||||
@synchronized(self.lock) {
|
||||
if (self.finished) {
|
||||
return;
|
||||
}
|
||||
[self.condition lock];
|
||||
}
|
||||
while (!self.finished) {
|
||||
[self.condition wait];
|
||||
}
|
||||
[self.condition unlock];
|
||||
}
|
||||
|
||||
- (void)setCredential:(QCloudCredential *)credential{
|
||||
|
||||
NSMutableDictionary * _payload = self.payload.mutableCopy;
|
||||
if (!_payload) {
|
||||
_payload = [NSMutableDictionary new];
|
||||
}
|
||||
if (credential) {
|
||||
[_payload setObject:credential forKey:@"QCloudCredential"];
|
||||
}
|
||||
self.payload = _payload.copy;
|
||||
}
|
||||
|
||||
- (QCloudCredential *)credential{
|
||||
return [self.payload objectForKey:@"QCloudCredential"];
|
||||
}
|
||||
|
||||
- (void)setShouldSignedList:(NSArray *)shouldSignedList{
|
||||
NSMutableDictionary * _payload = self.payload.mutableCopy;
|
||||
if (!_payload) {
|
||||
_payload = [NSMutableDictionary new];
|
||||
}
|
||||
if (shouldSignedList) {
|
||||
[_payload setObject:shouldSignedList forKey:@"shouldSignedList"];
|
||||
}
|
||||
self.payload = _payload.copy;
|
||||
}
|
||||
|
||||
- (NSArray *)shouldSignedList{
|
||||
return [self.payload objectForKey:@"shouldSignedList"];
|
||||
}
|
||||
|
||||
- (void)setSignature:(QCloudSignature *)signature{
|
||||
NSMutableDictionary * _payload = self.payload.mutableCopy;
|
||||
if (!_payload) {
|
||||
_payload = [NSMutableDictionary new];
|
||||
}
|
||||
if (signature) {
|
||||
[_payload setObject:signature forKey:@"signature"];
|
||||
}
|
||||
self.payload = _payload.copy;
|
||||
}
|
||||
|
||||
- (QCloudSignature *)signature{
|
||||
return [self.payload objectForKey:@"signature"];
|
||||
}
|
||||
|
||||
- (void)configTaskResume {
|
||||
}
|
||||
@end
|
||||
148
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest.h
generated
Normal file
148
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest.h
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// QCloudHTTPRequest.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/25.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudRequestData.h"
|
||||
#import "QCloudRequestSerializer.h"
|
||||
#import "QCloudResponseSerializer.h"
|
||||
#import "QCloudHTTPRequestDelegate.h"
|
||||
#import "QCloudAbstractRequest.h"
|
||||
@class QCloudHTTPRetryHanlder;
|
||||
@class QCloudHTTPSessionManager;
|
||||
@class QCloudService;
|
||||
|
||||
typedef void (^QCloudHTTPRequestConfigure)(QCloudRequestSerializer *_Nonnull requestSerializer,
|
||||
QCloudResponseSerializer *_Nonnull responseSerializer);
|
||||
|
||||
/**
|
||||
network base request
|
||||
*/
|
||||
@interface QCloudHTTPRequest : QCloudAbstractRequest {
|
||||
@protected
|
||||
QCloudRequestData *_requestData;
|
||||
QCloudRequestSerializer *_requestSerializer;
|
||||
QCloudResponseSerializer *_responseSerializer;
|
||||
QCloudHTTPRetryHanlder *_retryHandler;
|
||||
NSString *_serverDomain;
|
||||
}
|
||||
@property (nonatomic, strong, readonly) QCloudRequestSerializer *_Nonnull requestSerializer;
|
||||
@property (nonatomic, strong, readonly) QCloudRequestData *_Nonnull requestData;
|
||||
@property (nonatomic, strong, readonly) QCloudResponseSerializer *_Nonnull responseSerializer;
|
||||
@property (nonatomic, strong, readonly) NSURLRequest *_Nullable urlRequest;
|
||||
|
||||
/// sdk内部管理,业务测无需设置。
|
||||
@property (nonatomic, assign, readonly) BOOL isRetry;
|
||||
@property (nonatomic, assign) NSInteger retryCount;
|
||||
|
||||
/**
|
||||
该任务所处的服务
|
||||
*/
|
||||
@property (nonatomic, weak) QCloudService *_Nullable runOnService;
|
||||
|
||||
/**
|
||||
如果存在改参数,则数据会下载到改路径指名的地址下面,而不会写入内存中。
|
||||
*/
|
||||
@property (nonatomic, strong) NSURL *_Nonnull downloadingURL;
|
||||
|
||||
@property (nonatomic, strong , readonly) NSURL *_Nonnull downloadingTempURL;
|
||||
|
||||
/**
|
||||
本地已经下载的数据偏移量,如果使用则会从改位置开始下载,如果不使用,则从头开始下载,如果您使用了Range参数,则需要注意改参数。
|
||||
*/
|
||||
@property (nonatomic, assign) int64_t localCacheDownloadOffset;
|
||||
/**
|
||||
在特殊网络错误下,进行重试的策略,默认是不进行重试。可通过集成QCloudHTTPRetryHandler来自定义重试的出发条件和重试策略。
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) QCloudHTTPRetryHanlder *_Nullable retryPolicy;
|
||||
|
||||
/**
|
||||
服务器返回数据,当服务器有返回数据的时候,该字段有值,其他时候该字段无意义
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSData *_Nullable responseData;
|
||||
|
||||
/**
|
||||
服务器响应结构,当服务器有返回数据的时候,该字段有值,其他时候该字段无意义
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSHTTPURLResponse *_Nullable httpURLResponse;
|
||||
|
||||
/**
|
||||
当系统调用结束,并且出错的情况下,使用该字段表示错误信息,注意:只有在错误的情况下,该字段才会有数据
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSError *_Nullable httpURLError;
|
||||
|
||||
/**
|
||||
用来配置协议中HTTP的请求参数和解析
|
||||
*/
|
||||
@property (nonatomic, strong) QCloudHTTPRequestConfigure _Nonnull configureBlock;
|
||||
|
||||
/**
|
||||
当前的ConfiureBlock为空的时候,会调用该函数加载配置函数。
|
||||
*/
|
||||
- (void)loadConfigureBlock;
|
||||
|
||||
- (void)setConfigureBlock:(void (^_Nonnull)(QCloudRequestSerializer *_Nonnull requestSerializer,
|
||||
QCloudResponseSerializer *_Nonnull responseSerializer))configBlock;
|
||||
|
||||
/**
|
||||
构架RequestData,加载自定义的参数
|
||||
*/
|
||||
- (BOOL)buildRequestData:(NSError *_Nullable __autoreleasing *_Nullable)error;
|
||||
|
||||
/**
|
||||
构建真实网络请求需要的NSURLRequest
|
||||
|
||||
@param error 当出错的时候,表示出错信息
|
||||
@return 用于构建真实网络请求的NSURLRequest
|
||||
*/
|
||||
- (NSURLRequest *_Nullable)buildURLRequest:(NSError *_Nullable __autoreleasing *_Nullable)error;
|
||||
|
||||
@end
|
||||
|
||||
#define SUPER_BUILD_REUQSET_DATA \
|
||||
if (![super buildRequestData:error]) \
|
||||
return NO;
|
||||
|
||||
@interface QCloudHTTPRequest (SubClass)
|
||||
|
||||
/**
|
||||
将要开始发送请求的时候,将会调用该接口通知子类
|
||||
*/
|
||||
- (void)willStart;
|
||||
|
||||
/**
|
||||
加载错误重试策略,只有在第一次调用retryPolicy并且其值为空的时候会调用该方法来加载重试策略。子类可以重载该方法返回自己的重试策略。父类的默认行为是返回一个不进行重试的策略。
|
||||
*/
|
||||
- (void)loadRetryPolicy;
|
||||
|
||||
- (void)setConfigureBlock:(void (^_Nonnull)(QCloudRequestSerializer *_Nonnull requestSerializer,
|
||||
QCloudResponseSerializer *_Nonnull responseSerializer))confBlock;
|
||||
|
||||
- (BOOL)prepareInvokeURLRequest:(NSMutableURLRequest *_Nonnull)urlRequest error:(NSError *_Nullable __autoreleasing *_Nullable)error;
|
||||
@end
|
||||
|
||||
@class RNAsyncBenchMark;
|
||||
|
||||
#pragma deal with response
|
||||
@interface QCloudHTTPRequest ()
|
||||
|
||||
/**
|
||||
服务器返回response的时候处理,可以在这里做处理看是否接受后序的数据
|
||||
|
||||
@param response 服务器返回的response(主要包含头部)
|
||||
@return NSURLSessionResponseDisposition
|
||||
*/
|
||||
- (NSURLSessionResponseDisposition)reciveResponse:(NSURLResponse *_Nullable)response;
|
||||
|
||||
- (void)onReviveErrorResponse:(NSURLResponse *_Nullable)prsponse error:(NSError *_Nullable)error;
|
||||
- (void)onReciveRespone:(NSURLResponse *_Nullable)response data:(NSData *_Nullable)data;
|
||||
|
||||
-(BOOL)needChangeHost;
|
||||
|
||||
+(BOOL)needChangeHost:(NSString *_Nullable)host;
|
||||
|
||||
@end
|
||||
271
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest.m
generated
Normal file
271
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest.m
generated
Normal file
@@ -0,0 +1,271 @@
|
||||
//
|
||||
// QCloudHTTPRequest.m
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/25.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudHTTPRequest.h"
|
||||
#import "QCloudRequestData.h"
|
||||
#import "QCloudRequestSerializer.h"
|
||||
#import "QCloudHTTPRetryHanlder.h"
|
||||
#import "QCloudNetEnv.h"
|
||||
#import "QCloudHttpDNS.h"
|
||||
#import "QCloudIntelligenceTimeOutAdapter.h"
|
||||
#import "QCloudHTTPRequest_RequestID.h"
|
||||
#import "QCloudHttpMetrics.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "QCloudObjectModel.h"
|
||||
#import "QCloudSupervisory.h"
|
||||
#import "QCloudHTTPSessionManager.h"
|
||||
#import "NSError+QCloudNetworking.h"
|
||||
#import "QCLOUDRestNet.h"
|
||||
#import "QCloudService.h"
|
||||
|
||||
#import "NSDate+QCLOUD.h"
|
||||
#import "NSDate+QCloudInternetDateTime.h"
|
||||
#import "NSObject+HTTPHeadersContainer.h"
|
||||
@interface QCloudHTTPRequest () {
|
||||
BOOL _requesting;
|
||||
}
|
||||
@property (atomic, assign) BOOL isCancel;
|
||||
@property (nonatomic, strong, readonly) NSMutableURLRequest *cachedURLRequest;
|
||||
@property (nonatomic, strong, readonly) NSError *cachedURLRequestBuildError;
|
||||
@property (nonatomic, strong) NSURLRequest *_Nullable urlRequest;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudHTTPRequest
|
||||
@synthesize httpURLResponse = _httpURLResponse;
|
||||
@synthesize httpURLError = _httpURLError;
|
||||
|
||||
- (void)__baseCommonInit {
|
||||
_requestData = [QCloudRequestData new];
|
||||
_requestSerializer = [QCloudRequestSerializer new];
|
||||
_responseSerializer = [QCloudResponseSerializer new];
|
||||
_requesting = NO;
|
||||
_requestSerializer.timeoutInterval = [QCloudIntelligenceTimeOutAdapter recommendTimeOut];
|
||||
// if request is download request ,timeoutInterval = 30
|
||||
if (self.downloadingURL) {
|
||||
_requestSerializer.timeoutInterval = 30;
|
||||
}
|
||||
_isCancel = NO;
|
||||
}
|
||||
|
||||
- (NSHTTPURLResponse *)httpURLResponse {
|
||||
return _httpURLResponse;
|
||||
}
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self __baseCommonInit];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)notifyError:(NSError *)error {
|
||||
[super notifyError:error];
|
||||
[[QCloudSupervisory supervisory] recordRequest:self error:error];
|
||||
}
|
||||
|
||||
- (void)notifySuccess:(id)object {
|
||||
[super notifySuccess:object];
|
||||
[[QCloudSupervisory supervisory] recordRequest:self error:nil];
|
||||
}
|
||||
|
||||
- (void)loadConfigureBlock {
|
||||
[self setConfigureBlock:^(QCloudRequestSerializer *requestSerializer, QCloudResponseSerializer *responseSerializer) {
|
||||
requestSerializer.HTTPMethod = HTTPMethodGET;
|
||||
[requestSerializer setSerializerBlocks:@[ QCloudURLFuseSimple ]];
|
||||
//
|
||||
[responseSerializer setSerializerBlocks:@[ QCloudAcceptRespnseCodeBlock([NSSet setWithObject:@(200)], nil) ]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (QCloudHTTPRequestConfigure)configureBlock {
|
||||
if (!_configureBlock) {
|
||||
[self loadConfigureBlock];
|
||||
}
|
||||
return _configureBlock;
|
||||
}
|
||||
|
||||
- (void)willStart {
|
||||
QCloudLogDebugP(@"HTTP",@"[%llu] Will Start", self.requestID);
|
||||
}
|
||||
|
||||
- (void)loadRetryPolicy {
|
||||
_retryHandler = [QCloudHTTPRetryHanlder defaultRetryHandler];
|
||||
}
|
||||
- (QCloudHTTPRetryHanlder *)retryPolicy {
|
||||
if (!_retryHandler) {
|
||||
[self loadRetryPolicy];
|
||||
}
|
||||
return _retryHandler;
|
||||
}
|
||||
|
||||
- (BOOL)buildRequestData:(NSError *__autoreleasing *)error {
|
||||
__block QCloudRequestSerializer *reqSerializer = self.requestSerializer;
|
||||
__block QCloudResponseSerializer *rspSerializer = self.responseSerializer;
|
||||
if (self.configureBlock) {
|
||||
self.configureBlock(reqSerializer, rspSerializer);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)clearBuildCache {
|
||||
_cachedURLRequest = nil;
|
||||
_cachedURLRequestBuildError = nil;
|
||||
}
|
||||
|
||||
- (NSURL *)downloadingTempURL{
|
||||
if(_downloadingURL){
|
||||
return [NSURL URLWithString:[NSString stringWithFormat:@"%@.downloading",_downloadingURL.absoluteString]];
|
||||
}else{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURLRequest *)buildURLRequest:(NSError *__autoreleasing *)error {
|
||||
if (![self buildRequestData:error]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (self.isRetry) {
|
||||
[self.requestData setValue:@"true" forHTTPHeaderField:@"x-cos-sdk-retry"];
|
||||
}
|
||||
|
||||
[self.benchMarkMan benginWithKey:kCalculateMD5STookTime];
|
||||
NSURLRequest *request = [self.requestSerializer requestWithData:self.requestData error:error];
|
||||
if ([request.allHTTPHeaderFields objectForKey:@"Content-MD5"]) {
|
||||
[self.benchMarkMan markFinishWithKey:kCalculateMD5STookTime];
|
||||
}
|
||||
|
||||
if (*error) {
|
||||
QCloudLogErrorE(@"",@"[%@][%lld]序列化失败", self.class, self.requestID);
|
||||
return nil;
|
||||
}
|
||||
QCloudLogDebugP(@"HTTP",@"SendingRequest [%lld]\n%@\n%@ \nrequest content:%@", self.requestID, request, request.allHTTPHeaderFields,
|
||||
[[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding]);
|
||||
self.urlRequest = request;
|
||||
return request;
|
||||
}
|
||||
|
||||
- (void)onReviveErrorResponse:(NSURLResponse *)response error:(NSError *)error {
|
||||
_httpURLResponse = (NSHTTPURLResponse *)response;
|
||||
_httpURLError = error;
|
||||
if (NSURLErrorCancelled == error.code && [NSURLErrorDomain isEqualToString:error.domain]) {
|
||||
error = [NSError qcloud_errorWithCode:QCloudNetworkErrorCodeCanceled message:@"UserCancelled:The request is canceled" infos:@{kQCloudErrorDetailCode:@(NSURLErrorCancelled)}];
|
||||
}
|
||||
_httpURLError.__originHTTPURLResponse__ = _httpURLResponse;
|
||||
error.__originHTTPURLResponse__ = _httpURLResponse;
|
||||
[self onError:error];
|
||||
}
|
||||
- (void)onReciveRespone:(NSHTTPURLResponse *)response data:(NSData *)data {
|
||||
_responseData = data;
|
||||
_httpURLResponse = response;
|
||||
// //
|
||||
// {
|
||||
// NSUInteger headerLength = 0;
|
||||
// NSDictionary *allHeaders = nil;
|
||||
// if ([response respondsToSelector:@selector(allHeaderFields)]) {
|
||||
// allHeaders = [response allHeaderFields];
|
||||
// }
|
||||
// if (allHeaders) {
|
||||
// if (response.allHeaderFields) {
|
||||
// NSData *headerData = [NSJSONSerialization dataWithJSONObject:allHeaders options:0 error:0];
|
||||
// headerLength = headerData.length;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
NSString *dateStr = [[response allHeaderFields] objectForKey:@"Date"];
|
||||
NSDate *serverTime = nil;
|
||||
NSDate *deviceTime = [NSDate date];
|
||||
if ([dateStr length] > 0) {
|
||||
serverTime = [NSDate qcloud_dateFromRFC822String:dateStr];
|
||||
} else {
|
||||
// The response header does not have the 'Date' field.
|
||||
// This should not happen.
|
||||
QCloudLogErrorE(@"",@"Date header does not exist. Not able to fix the time");
|
||||
}
|
||||
|
||||
NSTimeInterval skewTime = 0;
|
||||
if (serverTime) {
|
||||
skewTime = [deviceTime timeIntervalSinceDate:serverTime];
|
||||
}
|
||||
// If the time difference between the device and the server is large, fix device time
|
||||
QCloudLogDebugP(@"HTTP",@"skewTime: %f", skewTime);
|
||||
if (skewTime >= 1 * 60) {
|
||||
[NSDate qcloud_setTimeDeviation:skewTime];
|
||||
}
|
||||
NSError *localError;
|
||||
id outputObject = [self.responseSerializer decodeWithWithResponse:response data:data error:&localError];
|
||||
if (localError) {
|
||||
localError.__originHTTPURLResponse__ = response;
|
||||
localError.__originHTTPResponseData__ = data;
|
||||
QCloudLogErrorE(@"HTTP",@"[%@][%lld] %@", [self class], self.requestID, localError);
|
||||
if ([self isFixTime:localError]) {
|
||||
[NSDate qcloud_setTimeDeviation:skewTime];
|
||||
}
|
||||
[self onError:localError];
|
||||
} else {
|
||||
QCloudLogDebugP(@"HTTP",@"[%@][%lld] RESPONSE \n%@ ", [self class], self.requestID, [outputObject qcloud_modelToJSONString]);
|
||||
[outputObject set__originHTTPURLResponse__:response];
|
||||
[outputObject set__originHTTPResponseData__:data];
|
||||
[self onSuccess:outputObject];
|
||||
}
|
||||
}
|
||||
|
||||
// Error code to be fix
|
||||
- (BOOL)isFixTime:(NSError *)error {
|
||||
if ([error.userInfo[@"Code"] isEqualToString:@"RequestTimeTooSkewed"]
|
||||
|| ([error.userInfo[@"Code"] isEqualToString:@"AccessDenied"] || [error.userInfo[@"Message"] isEqualToString:@"Request has expired"])) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
- (BOOL)prepareInvokeURLRequest:(NSMutableURLRequest *)urlRequest error:(NSError *__autoreleasing *)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[super cancel];
|
||||
[[QCloudHTTPSessionManager shareClient] cancelRequestWithID:(int)self.requestID];
|
||||
}
|
||||
|
||||
- (NSURLSessionResponseDisposition)reciveResponse:(NSURLResponse *)response {
|
||||
return NSURLSessionResponseAllow;
|
||||
}
|
||||
|
||||
-(BOOL)needChangeHost{
|
||||
NSString * host = self.urlRequest.URL.host;
|
||||
return [QCloudHTTPRequest needChangeHost:host];
|
||||
}
|
||||
|
||||
+(BOOL)needChangeHost:(NSString *)host{
|
||||
if(!host){
|
||||
return NO;
|
||||
}
|
||||
if([host rangeOfString:@".cos.accelerate.myqcloud.com"].length > 0){
|
||||
return NO;
|
||||
}
|
||||
|
||||
if([host rangeOfString:@"service.cos.myqcloud.com"].length > 0){
|
||||
return NO;
|
||||
}
|
||||
if([host rangeOfString:@".myqcloud.com"].length > 0 && [host rangeOfString:@"cos."].length > 0 && [host rangeOfString:@".cos."].length == 0){
|
||||
return NO;
|
||||
}
|
||||
|
||||
if([host rangeOfString:@".myqcloud.com"].length > 0 && [host rangeOfString:@".cos."].length > 0){
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setEndpoint:(QCloudEndPoint *)endpoint{
|
||||
super.endpoint = endpoint;
|
||||
self.requestData.endpoint = endpoint;
|
||||
}
|
||||
@end
|
||||
32
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequestDelegate.h
generated
Normal file
32
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequestDelegate.h
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// QCloudHTTPRequestDelegate.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/30.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^QCloudRequestFinishBlock)(id outputObject, NSError *error);
|
||||
|
||||
@class QCloudAbstractRequest;
|
||||
@protocol QCloudHTTPRequestDelegate <NSObject>
|
||||
@optional
|
||||
- (void)QCloudHTTPRequestDidFinished:(QCloudAbstractRequest *)request succeedWithObject:(id)object;
|
||||
- (void)QCloudHTTPRequestDidFinished:(QCloudAbstractRequest *)request failed:(NSError *)object;
|
||||
- (void)QCloudHTTPRequest:(QCloudAbstractRequest *)request
|
||||
sendBytes:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
|
||||
- (void)QCloudHTTPRequest:(QCloudAbstractRequest *)request
|
||||
bytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload;
|
||||
|
||||
- (void)QCloudHTTPRequest:(QCloudAbstractRequest *)request
|
||||
bytesDownload:(int64_t)bytesDownload
|
||||
totalBytesDownload:(int64_t)totalBytesDownload
|
||||
totalBytesExpectedToDownload:(int64_t)totalBytesExpectedToDownload
|
||||
receiveData:(NSData *)data;
|
||||
@end
|
||||
12
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest_RequestID.h
generated
Normal file
12
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudHTTPRequest_RequestID.h
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// QCloudHTTPRequest_RequestID.h
|
||||
// QCloudTernimalLab_CommonLogic
|
||||
//
|
||||
// Created by tencent on 5/23/16.
|
||||
// Copyright © 2016 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudHTTPRequest.h"
|
||||
|
||||
@interface QCloudHTTPRequest ()
|
||||
@end
|
||||
190
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudRequestData.h
generated
Normal file
190
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudRequestData.h
generated
Normal file
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// QCloudRequestData.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/24.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "QCloudHTTPMultiDataStream.h"
|
||||
#import "QCloudEndPoint.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
extern NSString *const HTTPHeaderUserAgent;
|
||||
extern NSString *const emergencyHost;
|
||||
|
||||
/**
|
||||
网络请求参数的容器类
|
||||
*/
|
||||
|
||||
@interface QCloudRequestData<BodyType> : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSDictionary *queryParamters;
|
||||
|
||||
/**
|
||||
指定接口级请求域名
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) QCloudEndPoint * endpoint;
|
||||
/**
|
||||
数据的编码格式
|
||||
*/
|
||||
@property (nonatomic, assign) NSStringEncoding stringEncoding;
|
||||
/**
|
||||
服务器地址
|
||||
*/
|
||||
@property (strong, nonatomic ,nullable) NSString *serverURL;
|
||||
|
||||
@property (strong, nonatomic ,nullable) NSString *bucket;
|
||||
|
||||
@property (strong, nonatomic ,nullable) NSString *appId;
|
||||
|
||||
@property (strong, nonatomic ,nullable) NSString *region;
|
||||
|
||||
/// 重试时是否需要更换域名
|
||||
@property (assign, nonatomic) BOOL needChangeHost;
|
||||
|
||||
/**
|
||||
* 统一资源标识符,用来标识调用的具体的资源地址
|
||||
*/
|
||||
@property (nonatomic, strong,nullable) NSString *URIMethod;
|
||||
|
||||
/**
|
||||
如果URIMethod不足以表示所有的命令字的时候,可以在改字段中按照顺序添加URI分片,将会按照顺序组装起来。
|
||||
*/
|
||||
@property (nonatomic, strong,nullable) NSArray *URIComponents;
|
||||
/**
|
||||
HTTP headers参数,用来配置Request
|
||||
*/
|
||||
@property (nonatomic, strong, nonnull, readonly) NSDictionary *httpHeaders;
|
||||
|
||||
/**
|
||||
所有的参数,填充在这里面的参数为不需要添加在HTTPHeaders里面的参数
|
||||
*/
|
||||
@property (nonnull, strong, nonatomic, readonly) NSDictionary *allParamters;
|
||||
|
||||
/**
|
||||
使用multipart/form-data上传数据时所需要的数据流,默认为空,只有当添加了form data后该字段才有值
|
||||
*/
|
||||
@property (nonatomic, strong, readonly, nullable) QCloudHTTPMultiDataStream *multiDataStream;
|
||||
|
||||
@property (nullable, strong) NSString *boundary;
|
||||
/**
|
||||
请求中要使用到的Cookies
|
||||
*/
|
||||
@property (nonatomic, strong, nullable, readonly) NSArray *cookies;
|
||||
|
||||
@property (nullable, strong) BodyType directBody;
|
||||
|
||||
/**
|
||||
清除所有参数
|
||||
*/
|
||||
- (void)clean;
|
||||
|
||||
/**
|
||||
添加类型为NSString的参数
|
||||
@param paramter 添加的参数
|
||||
@param key 关键字
|
||||
*/
|
||||
- (void)setParameter:(nonnull id)paramter withKey:(nonnull NSString *)key;
|
||||
|
||||
/**
|
||||
添加类型为NSNumber的请求参数
|
||||
@param paramter 添加的参数
|
||||
@param key 参数对应的关键字
|
||||
*/
|
||||
- (void)setNumberParamter:(nonnull NSNumber *)paramter withKey:(nonnull NSString *)key;
|
||||
|
||||
- (void)setQueryStringParamter:(nonnull NSString *)paramter withKey:(nonnull NSString *)key;
|
||||
/**
|
||||
通过指定的Key获取
|
||||
@param key 参数的Key
|
||||
@return 获取到的参数值
|
||||
*/
|
||||
|
||||
- (id)paramterForKey:(NSString *)key;
|
||||
/**
|
||||
添加HTTP header中的信息
|
||||
其中User-Agent等有默认信息
|
||||
@param value 要添加的信息
|
||||
@param field 对应的关键字
|
||||
*/
|
||||
- (void)setValue:(nonnull id)value forHTTPHeaderField:(nonnull NSString *)field;
|
||||
- (NSString *)valueForHttpKey:(NSString *)key;
|
||||
/**
|
||||
通过URL Paramater字符串的方式来添加参数
|
||||
@param paramters 参数字符串,按照xx=xx&xx=xx的形式
|
||||
*/
|
||||
- (void)setParamatersWithString:(nonnull NSString *)paramters;
|
||||
|
||||
/**
|
||||
通过字典的形式添加参数
|
||||
@param paramters 参数字典容器
|
||||
*/
|
||||
- (void)setParametersInDictionary:(nonnull NSDictionary *)paramters;
|
||||
|
||||
/**
|
||||
删除指定Key的Header
|
||||
@param key 要删除的Header
|
||||
*/
|
||||
- (void)removeHTTPHeaderForKey:(NSString *)key;
|
||||
/**
|
||||
手动添加Cookie
|
||||
@param domain 域
|
||||
@param path 路径
|
||||
@param name 名称
|
||||
@param value 值
|
||||
*/
|
||||
- (void)addCookieWithDomain:(nonnull NSString *)domain path:(nonnull NSString *)path name:(nonnull NSString *)name value:(nonnull id)value;
|
||||
|
||||
/**
|
||||
通过KeyValue形式添加FormData的参数
|
||||
|
||||
@param key key
|
||||
@param value value
|
||||
@return 是否添加成功
|
||||
*/
|
||||
- (BOOL)appendFormDataKey:(NSString *)key value:(NSString *)value;
|
||||
|
||||
/**
|
||||
添加文件内容部分
|
||||
|
||||
@param fileURL URL
|
||||
@param name 文件名
|
||||
@param fileName 文件名
|
||||
@param mimeType mimeType
|
||||
@param paramerts 头部参数
|
||||
@param error error
|
||||
@return 成功与否
|
||||
*/
|
||||
- (BOOL)appendPartWithFileURL:(nonnull NSURL *)fileURL
|
||||
name:(nonnull NSString *)name
|
||||
fileName:(nonnull NSString *)fileName
|
||||
mimeType:(nonnull NSString *)mimeType
|
||||
headerParamters:(nullable NSDictionary *)paramerts
|
||||
error:(NSError *_Nullable __autoreleasing *)error;
|
||||
|
||||
/**
|
||||
添加分片文件内容部分
|
||||
|
||||
@param fileURL url
|
||||
@param name name
|
||||
@param fileName fileName
|
||||
@param offset offset
|
||||
@param sliceLength sliceLength
|
||||
@param mimeType mimeType
|
||||
@param paramerts parameters
|
||||
@param error error
|
||||
@return 成功与否
|
||||
*/
|
||||
- (BOOL)appendPartWithFileURL:(nonnull NSURL *)fileURL
|
||||
name:(nonnull NSString *)name
|
||||
fileName:(nonnull NSString *)fileName
|
||||
offset:(int64_t)offset
|
||||
sliceLength:(int)sliceLength
|
||||
mimeType:(nonnull NSString *)mimeType
|
||||
headerParamters:(nullable NSDictionary *)paramerts
|
||||
error:(NSError *_Nullable __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
390
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudRequestData.m
generated
Normal file
390
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/QCloudRequestData.m
generated
Normal file
@@ -0,0 +1,390 @@
|
||||
//
|
||||
// QCloudRequestData.m
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/24.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudRequestData.h"
|
||||
#import "NSError+QCloudNetworking.h"
|
||||
#import "QCloudHTTPBodyPart.h"
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#define String_ENSURE_NOT_NIL_PARAMTER(p) \
|
||||
if (p == nil) \
|
||||
return nil;
|
||||
#define ENSURE_NOT_NIL_PARAMTER(p) \
|
||||
if (p == nil) \
|
||||
return;
|
||||
#define B_ENSURE_NOT_NIL_PARAMTER(p) \
|
||||
if (p == nil) \
|
||||
return NO;
|
||||
|
||||
|
||||
|
||||
NSDictionary *QCloudURLDecodePatamters(NSString *str) {
|
||||
NSRange rangeOfQ = [str rangeOfString:@"?"];
|
||||
NSString *subStr = str;
|
||||
if (rangeOfQ.location != NSNotFound) {
|
||||
subStr = [str substringFromIndex:rangeOfQ.location + rangeOfQ.length];
|
||||
}
|
||||
NSArray *coms = [subStr componentsSeparatedByString:@"&"];
|
||||
if (coms.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary *paramters = [NSMutableDictionary new];
|
||||
for (NSString *s in coms) {
|
||||
NSArray *kv = [s componentsSeparatedByString:@"="];
|
||||
if (kv.count != 2) {
|
||||
continue;
|
||||
}
|
||||
NSString *key = kv[0];
|
||||
NSString *value = kv[1];
|
||||
paramters[key] = value;
|
||||
}
|
||||
return paramters;
|
||||
}
|
||||
|
||||
NSString *const HTTPHeaderUserAgent = @"User-Agent";
|
||||
NSString *const emergencyHost = @"tencentcos.cn";
|
||||
|
||||
@interface QCloudRequestData () {
|
||||
NSMutableDictionary *_paramters;
|
||||
NSMutableDictionary *_httpHeaders;
|
||||
NSMutableDictionary *_queryParamters;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation QCloudRequestData
|
||||
@synthesize multiDataStream = _multiDataStream;
|
||||
- (void)loadDefaultHTTPHeaders {
|
||||
static NSDictionary *httpHeaders;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
#if TARGET_OS_IOS
|
||||
NSString *userAgent =
|
||||
[NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)",
|
||||
[[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey]
|
||||
?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey],
|
||||
[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]
|
||||
?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey],
|
||||
[[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
|
||||
#elif TARGET_OS_MAC
|
||||
NSString* userAgent = @"Test-Mac-Agent";
|
||||
#endif
|
||||
httpHeaders = @ { @"Connection" : @"keep-alive", HTTPHeaderUserAgent : userAgent };
|
||||
});
|
||||
_httpHeaders = [httpHeaders mutableCopy];
|
||||
}
|
||||
- (void)__dataCommonInit {
|
||||
_paramters = [NSMutableDictionary new];
|
||||
_cookies = [NSMutableArray new];
|
||||
_stringEncoding = NSUTF8StringEncoding;
|
||||
_queryParamters = [NSMutableDictionary new];
|
||||
[self loadDefaultHTTPHeaders];
|
||||
}
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self __dataCommonInit];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)queryParamters {
|
||||
return [_queryParamters copy];
|
||||
}
|
||||
|
||||
- (NSString *)URIMethod {
|
||||
if (!_URIMethod) {
|
||||
return @"";
|
||||
} else {
|
||||
return _URIMethod;
|
||||
}
|
||||
}
|
||||
- (NSDictionary *)allParamters {
|
||||
return [_paramters copy];
|
||||
}
|
||||
|
||||
- (void)setParameter:(nonnull id)paramter withKey:(nonnull NSString *)key {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(key);
|
||||
#else
|
||||
ENSURE_NOT_NIL_PARAMTER(paramter);
|
||||
#endif
|
||||
if (!paramter || [paramter isKindOfClass:[NSNull class]]) {
|
||||
return;
|
||||
}
|
||||
_paramters[key] = paramter;
|
||||
}
|
||||
|
||||
- (void)setNumberParamter:(nonnull NSNumber *)paramter withKey:(nonnull NSString *)key {
|
||||
[self setParameter:[paramter stringValue] withKey:key];
|
||||
}
|
||||
|
||||
- (void)setQueryStringParamter:(nonnull NSString *)paramter withKey:(nonnull NSString *)key {
|
||||
NSParameterAssert(key);
|
||||
if (!paramter || [paramter isKindOfClass:[NSNull class]]) {
|
||||
paramter = @"";
|
||||
}
|
||||
if (![paramter isKindOfClass:[NSString class]]) {
|
||||
paramter = [NSString stringWithFormat:@"%@", paramter];
|
||||
}
|
||||
_queryParamters[key] = paramter;
|
||||
}
|
||||
|
||||
- (void)setValue:(nonnull id)value forHTTPHeaderField:(nonnull NSString *)field {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(field);
|
||||
#else
|
||||
ENSURE_NOT_NIL_PARAMTER(field);
|
||||
#endif
|
||||
[_httpHeaders setValue:value forKey:field];
|
||||
}
|
||||
|
||||
- (NSString *)valueForHttpKey:(NSString *)key {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(key);
|
||||
#else
|
||||
String_ENSURE_NOT_NIL_PARAMTER(key);
|
||||
#endif
|
||||
return [_httpHeaders valueForKey:key];
|
||||
}
|
||||
|
||||
- (void)addCookieWithDomain:(NSString *)domain path:(NSString *)path name:(NSString *)name value:(id)value {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(domain);
|
||||
NSParameterAssert(path);
|
||||
NSParameterAssert(name);
|
||||
NSParameterAssert(value);
|
||||
#else
|
||||
ENSURE_NOT_NIL_PARAMTER(domain);
|
||||
ENSURE_NOT_NIL_PARAMTER(path);
|
||||
ENSURE_NOT_NIL_PARAMTER(name);
|
||||
ENSURE_NOT_NIL_PARAMTER(value);
|
||||
#endif
|
||||
NSDictionary *info = @{ NSHTTPCookieValue : value, NSHTTPCookieName : name, NSHTTPCookieDomain : domain, NSHTTPCookiePath : path };
|
||||
|
||||
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:info];
|
||||
|
||||
NSMutableArray *cookies = [self.cookies mutableCopy];
|
||||
NSInteger index = NSNotFound;
|
||||
|
||||
for (int i = 0; i < cookies.count; i++) {
|
||||
NSHTTPCookie *c = cookies[i];
|
||||
if ([c.domain isEqualToString:cookie.domain] && [c.path isEqualToString:cookie.path] && [c.name isEqualToString:cookie.name]) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != NSNotFound) {
|
||||
[cookies replaceObjectAtIndex:index withObject:cookie];
|
||||
} else {
|
||||
[cookies addObject:cookie];
|
||||
}
|
||||
_cookies = cookies;
|
||||
}
|
||||
|
||||
- (void)setParamatersWithString:(NSString *)paramters {
|
||||
NSDictionary *dic = QCloudURLDecodePatamters(paramters);
|
||||
NSAssert(dic, @"paramters 字符串解析出现问题,没有成功解析出字典");
|
||||
if (dic) {
|
||||
for (NSString *key in dic.allKeys) {
|
||||
[self setParameter:dic[key] withKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setParametersInDictionary:(NSDictionary *)paramters {
|
||||
if (paramters) {
|
||||
for (NSString *key in paramters.allKeys) {
|
||||
[self setParameter:paramters[key] withKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (QCloudHTTPMultiDataStream *)multiDataStream {
|
||||
if (!_multiDataStream) {
|
||||
_multiDataStream = [[QCloudHTTPMultiDataStream alloc] initWithStringEncoding:_stringEncoding];
|
||||
_multiDataStream.stringEncoding = _stringEncoding;
|
||||
}
|
||||
return _multiDataStream;
|
||||
}
|
||||
|
||||
- (BOOL)appendFormDataKey:(NSString *)key value:(NSString *)value {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(key);
|
||||
NSParameterAssert(value);
|
||||
#else
|
||||
B_ENSURE_NOT_NIL_PARAMTER(key);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(value);
|
||||
#endif
|
||||
if (![value isKindOfClass:[NSString class]]) {
|
||||
value = [NSString stringWithFormat:@"%@", value];
|
||||
}
|
||||
QCloudHTTPBodyPart *part = [[QCloudHTTPBodyPart alloc] initWithData:[value dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[part setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", key] forHeaderKey:@"Content-Disposition"];
|
||||
[[self multiDataStream] appendBodyPart:part];
|
||||
return YES;
|
||||
}
|
||||
- (BOOL)appendPartWithFileURL:(nonnull NSURL *)fileURL
|
||||
name:(nonnull NSString *)name
|
||||
fileName:(nonnull NSString *)fileName
|
||||
mimeType:(nonnull NSString *)mimeType
|
||||
headerParamters:(nullable NSDictionary *)paramerts
|
||||
error:(NSError *_Nullable __autoreleasing *)error;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(fileURL);
|
||||
NSParameterAssert(name);
|
||||
NSParameterAssert(fileName);
|
||||
NSParameterAssert(mimeType);
|
||||
#else
|
||||
B_ENSURE_NOT_NIL_PARAMTER(fileURL);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(name);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(fileName);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(mimeType);
|
||||
#endif
|
||||
|
||||
if (![fileURL isFileURL]) {
|
||||
if (error) {
|
||||
*error = [NSError qcloud_errorWithCode:NSURLErrorBadURL
|
||||
message:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"QCloudNetworking", nil)];
|
||||
}
|
||||
|
||||
return NO;
|
||||
} else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
|
||||
if (error) {
|
||||
*error = [NSError qcloud_errorWithCode:NSURLErrorBadURL message:[NSString stringWithFormat:@"File URL not reachable. %@", fileURL]];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
|
||||
if (!fileAttributes) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
QCloudHTTPBodyPart *part = [[QCloudHTTPBodyPart alloc] initWithURL:fileURL withContentLength:[fileAttributes[NSFileSize] unsignedLongLongValue]];
|
||||
|
||||
[part setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forHeaderKey:@"Content-Disposition"];
|
||||
|
||||
[part setHeaderValueWithMap:paramerts];
|
||||
[part setValue:mimeType forHeaderKey:@"Content-Type"];
|
||||
|
||||
[[self multiDataStream] appendBodyPart:part];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)appendPartWithFileURL:(nonnull NSURL *)fileURL
|
||||
name:(nonnull NSString *)name
|
||||
fileName:(nonnull NSString *)fileName
|
||||
offset:(int64_t)offset
|
||||
sliceLength:(int)sliceLength
|
||||
mimeType:(nonnull NSString *)mimeType
|
||||
headerParamters:(nullable NSDictionary *)paramerts
|
||||
error:(NSError *_Nullable __autoreleasing *)error {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(fileURL);
|
||||
NSParameterAssert(name);
|
||||
NSParameterAssert(fileName);
|
||||
NSParameterAssert(mimeType);
|
||||
#else
|
||||
B_ENSURE_NOT_NIL_PARAMTER(fileURL);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(name);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(fileName);
|
||||
B_ENSURE_NOT_NIL_PARAMTER(mimeType);
|
||||
#endif
|
||||
|
||||
if (![fileURL isFileURL]) {
|
||||
if (error) {
|
||||
*error = [NSError qcloud_errorWithCode:NSURLErrorBadURL
|
||||
message:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"QCloudNetworking", nil)];
|
||||
}
|
||||
|
||||
return NO;
|
||||
} else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
|
||||
if (error) {
|
||||
*error = [NSError qcloud_errorWithCode:NSURLErrorBadURL message:[NSString stringWithFormat:@"File URL not reachable. %@", fileURL]];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
|
||||
if (!fileAttributes) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
QCloudHTTPBodyPart *part = [[QCloudHTTPBodyPart alloc] initWithURL:fileURL offetSet:offset withContentLength:sliceLength];
|
||||
[part setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forHeaderKey:@"Content-Disposition"];
|
||||
|
||||
[part setHeaderValueWithMap:paramerts];
|
||||
[part setValue:mimeType forHeaderKey:@"Content-Type"];
|
||||
|
||||
[[self multiDataStream] appendBodyPart:part];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setStringEncoding:(NSStringEncoding)stringEncoding {
|
||||
_stringEncoding = stringEncoding;
|
||||
_multiDataStream.stringEncoding = stringEncoding;
|
||||
}
|
||||
|
||||
- (id)paramterForKey:(NSString *)key {
|
||||
return [_paramters objectForKey:key];
|
||||
}
|
||||
|
||||
- (void)removeHTTPHeaderForKey:(NSString *)key {
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
[_httpHeaders removeObjectForKey:key];
|
||||
}
|
||||
|
||||
- (void)clean {
|
||||
_queryParamters = nil;
|
||||
self.stringEncoding = NSUTF8StringEncoding;
|
||||
_serverURL = nil;
|
||||
_URIMethod = nil;
|
||||
_URIComponents = nil;
|
||||
_httpHeaders = nil;
|
||||
_paramters = nil;
|
||||
_multiDataStream = nil;
|
||||
_boundary = nil;
|
||||
_cookies = nil;
|
||||
_directBody = nil;
|
||||
[self __dataCommonInit];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *str = [NSMutableString new];
|
||||
if (self.httpHeaders.count) {
|
||||
[str appendFormat:@"[HEADERS] \n%@\n", self.httpHeaders];
|
||||
}
|
||||
if (self.allParamters.count) {
|
||||
[str appendFormat:@"[PARAMTERS] \n%@\n", self.allParamters];
|
||||
}
|
||||
if (self.multiDataStream.hasData) {
|
||||
[str appendFormat:@"[MULTIDATA] \n%@\n", self.multiDataStream];
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
- (void)setServerURL:(NSString *)serverURL{
|
||||
if (self.endpoint) {
|
||||
_serverURL = [self.endpoint serverURLWithBucket:self.bucket appID:self.appId regionName:self.region].absoluteString;
|
||||
}else if(self.needChangeHost){
|
||||
_serverURL = [serverURL stringByReplacingOccurrencesOfString:@"myqcloud.com" withString:emergencyHost];
|
||||
}else{
|
||||
_serverURL = serverURL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
129
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudRequestSerializer.h
generated
Normal file
129
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudRequestSerializer.h
generated
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// QCloudRequestSerializer.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/23.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@class QCloudRequestData;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
FOUNDATION_EXTERN NSString *QCloudStrigngURLEncode(NSString *string, NSStringEncoding stringEncoding);
|
||||
FOUNDATION_EXTERN NSString *QCloudURLEncodeParamters(NSDictionary *dic, BOOL willUrlEncoding, NSStringEncoding stringEncoding);
|
||||
FOUNDATION_EXTERN NSString *QCloudURLEncodeUTF8(NSString *string);
|
||||
FOUNDATION_EXTERN NSString *QCloudURLDecodeUTF8(NSString *string);
|
||||
FOUNDATION_EXTERN NSString *QCloudNSURLEncode(NSString *url);
|
||||
FOUNDATION_EXTERN NSDictionary *QCloudURLReadQuery(NSURL *url);
|
||||
/**
|
||||
HTTP POST 方法
|
||||
*/
|
||||
extern NSString *const HTTPMethodPOST;
|
||||
/**
|
||||
HTTP GET方法
|
||||
*/
|
||||
extern NSString *const HTTPMethodGET;
|
||||
|
||||
extern NSString *const HTTPHeaderHOST;
|
||||
|
||||
@class QCloudRequestData;
|
||||
|
||||
typedef NSMutableURLRequest *_Nullable (^QCloudRequestSerializerBlock)(NSMutableURLRequest *request, QCloudRequestData *data,
|
||||
NSError *__autoreleasing *error);
|
||||
|
||||
/**
|
||||
进行Request参数拼装的类,此类可以配置HTTP相关的一些参数,也可以配置协议相关的一些参数
|
||||
*/
|
||||
@interface QCloudRequestSerializer : NSObject
|
||||
@property (nonnull, nonatomic, strong) NSString *HTTPMethod;
|
||||
@property (nonatomic, assign) BOOL useCookies;
|
||||
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
|
||||
/**
|
||||
是否开启HTTPS验证,默认为YES
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL shouldAuthentication;
|
||||
|
||||
/**
|
||||
是否处理cookies
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
|
||||
|
||||
/**
|
||||
是否开启pipeline功能
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
|
||||
|
||||
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
|
||||
|
||||
/**
|
||||
设置根据requestData对请求的URL进行拼装的功能
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) NSArray<QCloudRequestSerializerBlock> *serializerBlocks;
|
||||
|
||||
/**
|
||||
是否开启GZIP压缩Response
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowCompressedResponse;
|
||||
|
||||
/**
|
||||
是否使用HTTPDNSPrefetch功能获取到IP
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL HTTPDNSPrefetch;
|
||||
|
||||
- (NSMutableURLRequest *_Nullable)requestWithData:(QCloudRequestData *)data error:(NSError *__autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
按照Get请求拼参的方式,将所有参数和URL拼接到URL中,并获得URLRequet
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLAssembleWithParamters;
|
||||
/**
|
||||
只拼接ServerURL和MethodURL部分,组成一个URL,并获得URLRequest
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseSimple;
|
||||
/**
|
||||
在URL尾部按照?xx=xx&y=y的方式将所有参数拼接,并获得URLRequest, @note 使用该方法将不会对Value进行URLEncode
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseWithParamters;
|
||||
/**
|
||||
在URL尾部按照?xx=xx&y=y的方式将所有参数拼接,并获得URLRequest, @note 使用该方法将会对Value进行URLEncode
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseWithURLEncodeParamters;
|
||||
/**
|
||||
将所有参数按照xx=x&y=sdf的格式拼接在包体中,并返回响应URLRequest
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLSerilizerURLEncodingBody;
|
||||
/**
|
||||
清除所有的头部参数
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLCleanAllHeader;
|
||||
|
||||
/**
|
||||
将所有body参数按照JSON方式拼接到HTTPBody中,并设置content-type为application/json
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseWithJSONParamters;
|
||||
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudFuseMultiFormData;
|
||||
|
||||
/**
|
||||
按照formdata方式将参数拼入到formdata中
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudFuseParamtersASMultiData;
|
||||
|
||||
/**
|
||||
将一个KeyValueMap品入URL之中
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLSerilizerAppendURLParamters(NSDictionary *keyValueMaps);
|
||||
|
||||
/**
|
||||
将requestData的URIMethod字段按照URL Paramters的形式拼装入url中
|
||||
*/
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseURIMethodASURLParamters;
|
||||
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseWithXMLParamters;
|
||||
|
||||
extern _Nonnull QCloudRequestSerializerBlock QCloudURLFuseContentMD5Base64StyleHeaders;
|
||||
NS_ASSUME_NONNULL_END
|
||||
535
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudRequestSerializer.m
generated
Normal file
535
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudRequestSerializer.m
generated
Normal file
@@ -0,0 +1,535 @@
|
||||
//
|
||||
// QCloudRequestSerializer.m
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/23.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
#import "QCloudRequestSerializer.h"
|
||||
#import "QCloudRequestData.h"
|
||||
#import "NSHTTPCookie+QCloudNetworking.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import "QCloudReachability.h"
|
||||
#import "QCloudHTTPBodyPart.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
#import "QCloudWeakProxy.h"
|
||||
#import "QCloudXMLDictionary.h"
|
||||
#import "QCloudEncryt.h"
|
||||
#import "QCloudFileOffsetBody.h"
|
||||
#import "QCloudURLHelper.h"
|
||||
NSString *const HTTPMethodPOST = @"POST";
|
||||
NSString *const HTTPMethodGET = @"GET";
|
||||
NSString *const HTTPHeaderHOST = @"HOST";
|
||||
NSString *const HTTPHeaderContentType = @"Content-Type";
|
||||
NSString *const HTTPHeaderContentTypeURLEncode = @"application/x-www-form-urlencoded; charset=utf-8";
|
||||
|
||||
inline NSString *QCloudEnuserNoneNullString(NSString *str) {
|
||||
if (!str) {
|
||||
return @"";
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *QCloudStrigngURLEncode(NSString *string, NSStringEncoding stringEncoding) {
|
||||
NSString *escaped_value = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
|
||||
NULL, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^`"), CFStringConvertNSStringEncodingToEncoding(stringEncoding)));
|
||||
if (escaped_value) {
|
||||
return escaped_value;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
NSString *QCloudStrigngURLEncodeWithoutSpecials(NSString *string, NSStringEncoding stringEncoding) {
|
||||
NSString *escaped_value = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
|
||||
NULL, (CFStringRef)string, NULL, CFSTR("?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(stringEncoding)));
|
||||
if (escaped_value) {
|
||||
return escaped_value;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
NSString *QCloudStringURLDecode(NSString *string, NSStringEncoding encoding) {
|
||||
NSString *decoded = [string stringByReplacingPercentEscapesUsingEncoding:encoding];
|
||||
return decoded;
|
||||
}
|
||||
|
||||
NSString *QCloudURLEncodeUTF8(NSString *string) {
|
||||
return QCloudStrigngURLEncode(string, NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
NSString *QCloudURLDecodeUTF8(NSString *string) {
|
||||
return QCloudStringURLDecode(string, NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
NSString *QCloudNSURLEncode(NSString *url) {
|
||||
url = url.lowercaseString;
|
||||
NSArray *schemes = @[ @"http://", @"https://" ];
|
||||
for (NSString *scheme in schemes) {
|
||||
if ([url hasPrefix:scheme]) {
|
||||
url = [url substringFromIndex:scheme.length];
|
||||
url = [NSString stringWithFormat:@"%@%@", scheme, QCloudStrigngURLEncodeWithoutSpecials(url, NSUTF8StringEncoding)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
NSString *QCloudEncodeURL(NSString *url) {
|
||||
BOOL hasSubfix = [url hasSuffix:@"/"];
|
||||
NSString *http = @"http://";
|
||||
NSString *https = @"https://";
|
||||
NSString *prefix = @"";
|
||||
if ([url.lowercaseString hasPrefix:http]) {
|
||||
url = [url substringFromIndex:http.length];
|
||||
prefix = http;
|
||||
} else if ([url.lowercaseString hasPrefix:https]) {
|
||||
url = [url substringFromIndex:https.length];
|
||||
prefix = https;
|
||||
}
|
||||
NSArray *compnents = [url componentsSeparatedByString:@"/"];
|
||||
NSString *path = prefix;
|
||||
for (NSString *component in compnents) {
|
||||
path = QCloudPathJoin(path, QCloudStrigngURLEncode(component, NSUTF8StringEncoding));
|
||||
}
|
||||
if (hasSubfix) {
|
||||
path = QCloudPathJoin(path, @"/");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
NSDictionary *QCloudURLReadQuery(NSURL *url) {
|
||||
NSString *query = url.query;
|
||||
if (!query) {
|
||||
return @ {};
|
||||
}
|
||||
NSMutableDictionary *queryDic = [NSMutableDictionary new];
|
||||
NSArray *keyvalues = [query componentsSeparatedByString:@"&"];
|
||||
for (NSString *kv in keyvalues) {
|
||||
if (!kv.length) {
|
||||
continue;
|
||||
}
|
||||
NSArray <NSString *>*vs = [kv componentsSeparatedByString:@"="];
|
||||
if (vs.count == 2) {
|
||||
if(vs.lastObject.length>0){
|
||||
queryDic[QCloudStringURLDecode(vs[0], NSUTF8StringEncoding)] = QCloudStringURLDecode(vs[1], NSUTF8StringEncoding);
|
||||
}
|
||||
} else if (vs.count == 1) {
|
||||
queryDic[QCloudStringURLDecode(vs.firstObject, NSUTF8StringEncoding)] = @"";
|
||||
}
|
||||
}
|
||||
return queryDic;
|
||||
}
|
||||
|
||||
NSString *QCloudURLEncodeParamters(NSDictionary *dic, BOOL willUrlEncoding, NSStringEncoding stringEncoding) {
|
||||
NSArray *allKeys = dic.allKeys;
|
||||
allKeys = [allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
|
||||
return [obj1 compare:obj2];
|
||||
}];
|
||||
NSMutableString *path = [NSMutableString new];
|
||||
for (int i = 0; i < allKeys.count; i++) {
|
||||
if (i > 0) {
|
||||
[path appendString:@"&"];
|
||||
}
|
||||
NSString *key = allKeys[i];
|
||||
NSString *value = dic[key];
|
||||
if (willUrlEncoding) {
|
||||
key = QCloudStrigngURLEncode(key, stringEncoding);
|
||||
value = QCloudStrigngURLEncode(value, stringEncoding);
|
||||
}
|
||||
|
||||
NSString *segement = [NSString stringWithFormat:@"%@=%@", key, value];
|
||||
[path appendString:segement];
|
||||
}
|
||||
return [path copy];
|
||||
}
|
||||
|
||||
NSString *QCloudURLAppendParamters(NSString *base, NSString *paramters) {
|
||||
if (paramters.length == 0) {
|
||||
return base;
|
||||
}
|
||||
if ([paramters hasPrefix:@"?"]) {
|
||||
paramters = [paramters substringFromIndex:1];
|
||||
}
|
||||
|
||||
NSRange range = [base rangeOfString:@"?"];
|
||||
if (range.location != NSNotFound) {
|
||||
if ([base hasSuffix:@"?"]) {
|
||||
return [NSString stringWithFormat:@"%@%@", base, paramters];
|
||||
} else {
|
||||
if ([base hasSuffix:@"&"]) {
|
||||
return [NSString stringWithFormat:@"%@%@", base, paramters];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"%@&%@", base, paramters];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"%@?%@", base, paramters];
|
||||
}
|
||||
}
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLAssembleWithParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSString *path = QCloudPathJoin(data.serverURL, data.URIMethod);
|
||||
path = QCloudURLAppendParamters(path, QCloudURLEncodeParamters(data.allParamters, NO, data.stringEncoding));
|
||||
NSURL *url = [NSURL URLWithString:path];
|
||||
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
return urlRequest;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudFuseParamtersASMultiData
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSArray *keys = data.allParamters.allKeys;
|
||||
for (NSString *key in keys) {
|
||||
NSString *value = data.allParamters[key];
|
||||
NSCAssert([value isKindOfClass:[NSString class]], @"请传入NSString类型的Value Key:%@ Value:%@", key, value);
|
||||
NSData *indata = [value dataUsingEncoding:data.stringEncoding];
|
||||
QCloudHTTPBodyPart *part = [[QCloudHTTPBodyPart alloc] initWithData:indata];
|
||||
[part setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", key] forHeaderKey:@"Content-Disposition"];
|
||||
[data.multiDataStream insertBodyPart:part];
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudFuseMultiFormData = ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
if (data.multiDataStream.hasData) {
|
||||
[data.multiDataStream setInitialAndFinalBoundaries];
|
||||
[request setHTTPBodyStream:(NSInputStream *)[QCloudWeakProxy proxyWithTarget:data.multiDataStream]];
|
||||
|
||||
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", data.multiDataStream.boundary]
|
||||
forHTTPHeaderField:@"Content-Type"];
|
||||
[request setValue:[NSString stringWithFormat:@"%llu", [data.multiDataStream contentLength]] forHTTPHeaderField:@"Content-Length"];
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
NSString *QCloudURLFuseAllPathComponents(NSArray *componets) {
|
||||
NSString *path = @"";
|
||||
for (NSString *com in componets) {
|
||||
if (com.length > 0) {
|
||||
path = QCloudPathJoin(path, com);
|
||||
}
|
||||
}
|
||||
path = QCloudPercentEscapedStringFromString(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseSimple = ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSMutableArray *coms = [NSMutableArray new];
|
||||
if (data.URIMethod.length) {
|
||||
[coms addObject:data.URIMethod];
|
||||
}
|
||||
if (data.URIComponents.count) {
|
||||
[coms addObjectsFromArray:data.URIComponents];
|
||||
}
|
||||
NSString *path = QCloudURLFuseAllPathComponents(coms);
|
||||
path = QCloudPathJoin(data.serverURL, path);
|
||||
NSURL *url = [NSURL URLWithString:path];
|
||||
if (nil == url) {
|
||||
url = [NSURL URLWithString:QCloudURLEncodeUTF8(path)];
|
||||
}
|
||||
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
return urlRequest;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseWithParamters = ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSString *path = QCloudPathJoin(data.serverURL, data.URIMethod);
|
||||
path = QCloudURLAppendParamters(path, QCloudURLEncodeParamters(data.allParamters, NO, data.stringEncoding));
|
||||
NSURL *url = [NSURL URLWithString:path];
|
||||
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
return urlRequest;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseWithJSONParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data.allParamters options:NSJSONWritingPrettyPrinted error:error];
|
||||
if (*error) {
|
||||
return (NSMutableURLRequest *)nil;
|
||||
}
|
||||
[request setValue:[NSString stringWithFormat:@"application/json"] forHTTPHeaderField:@"Content-Type"];
|
||||
[request setValue:[@([jsonData length]) stringValue] forHTTPHeaderField:@"Content-Length"];
|
||||
[request setHTTPBody:jsonData];
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseWithXMLParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
if (data.allParamters.count) {
|
||||
NSString *str = [data.allParamters qcxml_XMLString];
|
||||
[request setValue:[NSString stringWithFormat:@"application/xml"] forHTTPHeaderField:@"Content-Type"];
|
||||
NSData *bodyData = [str dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[request setValue:[@([bodyData length]) stringValue] forHTTPHeaderField:@"Content-Length"];
|
||||
[request setHTTPBody:bodyData];
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseWithURLEncodeParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSString *urlStr = nil;
|
||||
if (request.URL.absoluteString.length > 0) {
|
||||
urlStr = request.URL.absoluteString;
|
||||
} else {
|
||||
urlStr = QCloudPathJoin(data.serverURL, data.URIMethod);
|
||||
}
|
||||
urlStr = QCloudURLAppendParamters(urlStr, QCloudURLEncodeParamters(data.allParamters, YES, data.stringEncoding));
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
return urlRequest;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseURIMethodASURLParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSString *urlStr = nil;
|
||||
if (request.URL.absoluteString.length > 0) {
|
||||
urlStr = request.URL.absoluteString;
|
||||
} else {
|
||||
urlStr = data.serverURL;
|
||||
NSMutableArray *coms = [NSMutableArray new];
|
||||
if (data.URIComponents.count) {
|
||||
[coms addObjectsFromArray:data.URIComponents];
|
||||
}
|
||||
NSString *path = QCloudURLFuseAllPathComponents(coms);
|
||||
urlStr = QCloudPathJoin(urlStr, path);
|
||||
}
|
||||
|
||||
NSMutableDictionary *methodParamters = [NSMutableDictionary new];
|
||||
if (data.URIMethod) {
|
||||
methodParamters[data.URIMethod] = @"";
|
||||
urlStr = QCloudURLAppendParamters(urlStr, QCloudURLEncodeParamters(methodParamters, YES, data.stringEncoding));
|
||||
if (urlStr.length && [urlStr hasSuffix:@"="]) {
|
||||
urlStr = [urlStr substringToIndex:urlStr.length - 1];
|
||||
}
|
||||
}
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
if (!request) {
|
||||
request = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
} else {
|
||||
[request setURL:url];
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLFuseContentMD5Base64StyleHeaders
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
if (request.HTTPBody) {
|
||||
NSData *data = request.HTTPBody;
|
||||
NSString *md5 = QCloudEncrytNSDataMD5Base64(data);
|
||||
if (md5) {
|
||||
[request setValue:md5 forHTTPHeaderField:@"Content-MD5"];
|
||||
}
|
||||
} else if (data.directBody) {
|
||||
if ([data.directBody isKindOfClass:[NSData class]]) {
|
||||
NSData *md5data = data.directBody;
|
||||
NSString *md5 = QCloudEncrytNSDataMD5Base64(md5data);
|
||||
if (md5) {
|
||||
[request setValue:md5 forHTTPHeaderField:@"Content-MD5"];
|
||||
}
|
||||
} else if ([data.directBody isKindOfClass:[NSURL class]]) {
|
||||
NSString *md5 = QCloudEncrytFileMD5Base64([(NSURL *)data.directBody path]);
|
||||
if (md5) {
|
||||
[request setValue:md5 forHTTPHeaderField:@"Content-MD5"];
|
||||
}
|
||||
} else if ([data.directBody isKindOfClass:[QCloudFileOffsetBody class]]) {
|
||||
QCloudFileOffsetBody *body = (QCloudFileOffsetBody *)data.directBody;
|
||||
NSString *md5 = QCloudEncrytFileOffsetMD5Base64(body.fileURL.path, body.offset, body.sliceLength);
|
||||
if (md5) {
|
||||
[request setValue:md5 forHTTPHeaderField:@"Content-MD5"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLSerilizerHTTPHeaderParamters
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSDictionary *allParamters = data.allParamters;
|
||||
NSArray *allKeys = allParamters.allKeys;
|
||||
for (NSString *key in allKeys) {
|
||||
[request setValue:allParamters[key] forHTTPHeaderField:key];
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLSerilizerAppendURLParamters(NSDictionary *keyValueMaps) {
|
||||
return ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSString *urlStr = nil;
|
||||
if (request.URL.absoluteString.length > 0) {
|
||||
urlStr = request.URL.absoluteString;
|
||||
} else {
|
||||
urlStr = QCloudPathJoin(data.serverURL, data.URIMethod);
|
||||
}
|
||||
urlStr = QCloudURLAppendParamters(urlStr, QCloudURLEncodeParamters(keyValueMaps, YES, data.stringEncoding));
|
||||
if (urlStr.length && [urlStr hasSuffix:@"="]) {
|
||||
urlStr = [urlStr substringToIndex:urlStr.length - 1];
|
||||
}
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
NSMutableURLRequest *urlRequest;
|
||||
if (request) {
|
||||
urlRequest = [request mutableCopy];
|
||||
[urlRequest setURL:url];
|
||||
} else {
|
||||
urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
}
|
||||
return urlRequest;
|
||||
};
|
||||
}
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLSerilizerURLEncodingBody
|
||||
= ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
NSDictionary *allParamters = data.allParamters;
|
||||
NSString *content = QCloudURLEncodeParamters(allParamters, YES, data.stringEncoding);
|
||||
NSData *contentData = [content dataUsingEncoding:data.stringEncoding];
|
||||
[request setHTTPBody:contentData];
|
||||
[request setValue:HTTPHeaderContentTypeURLEncode forHTTPHeaderField:HTTPHeaderContentType];
|
||||
[request setValue:[@(contentData.length) stringValue] forHTTPHeaderField:@"Content-Length"];
|
||||
return request;
|
||||
};
|
||||
|
||||
QCloudRequestSerializerBlock QCloudURLCleanAllHeader = ^(NSMutableURLRequest *request, QCloudRequestData *data, NSError *__autoreleasing *error) {
|
||||
[request setValue:nil forHTTPHeaderField:@"Accept-Encoding"];
|
||||
[request setValue:nil forHTTPHeaderField:@"Connection"];
|
||||
[request setValue:nil forHTTPHeaderField:@"Cookie"];
|
||||
[request setValue:@"" forHTTPHeaderField:HTTPHeaderUserAgent];
|
||||
return request;
|
||||
};
|
||||
|
||||
static NSArray *QCloudHTTPReqeustSerializerObservedKeyPath() {
|
||||
static NSArray *paths = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
paths = @[
|
||||
NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)),
|
||||
NSStringFromSelector(@selector(HTTPShouldSetCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)),
|
||||
NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))
|
||||
];
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
static void *QCloudHTTPRequestSerializerObserverContext = &QCloudHTTPRequestSerializerObserverContext;
|
||||
|
||||
@interface QCloudRequestSerializer () {
|
||||
NSMutableSet *_mutableChangedPaths;
|
||||
NSDictionary *_defaultHTTPHeaders;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation QCloudRequestSerializer
|
||||
@synthesize shouldAuthentication = _shouldAuthentication;
|
||||
- (void)dealloc {
|
||||
for (NSString *selector in QCloudHTTPReqeustSerializerObservedKeyPath()) {
|
||||
if ([self respondsToSelector:NSSelectorFromString(selector)]) {
|
||||
@try {
|
||||
[self removeObserver:self forKeyPath:selector];
|
||||
} @catch (NSException *exception) {
|
||||
QCloudLogDebugE(@"Utils",@"没有该观察者");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)__commonInit {
|
||||
//
|
||||
_mutableChangedPaths = [NSMutableSet new];
|
||||
for (NSString *keyPath in QCloudHTTPReqeustSerializerObservedKeyPath()) {
|
||||
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
|
||||
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:QCloudHTTPRequestSerializerObserverContext];
|
||||
}
|
||||
}
|
||||
//
|
||||
_HTTPMethod = HTTPMethodGET;
|
||||
_allowCompressedResponse = NO;
|
||||
_serializerBlocks = @[ QCloudURLFuseWithParamters ];
|
||||
_HTTPDNSPrefetch = YES;
|
||||
_useCookies = YES;
|
||||
_shouldAuthentication = YES;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self __commonInit];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
|
||||
if (context == QCloudHTTPRequestSerializerObserverContext) {
|
||||
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
|
||||
[_mutableChangedPaths removeObject:keyPath];
|
||||
} else {
|
||||
[_mutableChangedPaths addObject:keyPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)requestWithData:(QCloudRequestData *)data error:(NSError *__autoreleasing *)error {
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
request.cachePolicy = self.cachePolicy;
|
||||
request.timeoutInterval = self.timeoutInterval;
|
||||
|
||||
NSAssert(self.serializerBlocks.count != 0, @"没有添加任何的序列化匿名函数,请检查配置!!!");
|
||||
NSError *localError;
|
||||
for (QCloudRequestSerializerBlock sBlock in self.serializerBlocks) {
|
||||
request = sBlock(request, data, &localError);
|
||||
|
||||
if (localError != nil) {
|
||||
if (error != NULL) {
|
||||
*error = localError;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request || *error) {
|
||||
if (error != NULL) {
|
||||
*error = [NSError errorWithDomain:@"com.tencent.qcloud.error"
|
||||
code:-1112
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"对request进行配置的时候出错,请检查所有的配置Block" }];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
request.HTTPMethod = self.HTTPMethod;
|
||||
//
|
||||
for (NSString *keyPath in QCloudHTTPReqeustSerializerObservedKeyPath()) {
|
||||
if ([_mutableChangedPaths containsObject:keyPath]) {
|
||||
[request setValue:[self valueForKey:keyPath] forKey:keyPath];
|
||||
}
|
||||
}
|
||||
//
|
||||
if (data.queryParamters.count > 0) {
|
||||
NSURL *url = request.URL;
|
||||
NSString *urlString = url.absoluteString;
|
||||
urlString = QCloudURLAppendParamters(urlString, QCloudURLEncodeParamters(data.queryParamters, YES, data.stringEncoding));
|
||||
url = [NSURL URLWithString:urlString];
|
||||
[request setURL:url];
|
||||
}
|
||||
//
|
||||
// http headers
|
||||
if (self.allowCompressedResponse) {
|
||||
[data setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
//
|
||||
NSDictionary *headers = [data.httpHeaders copy];
|
||||
NSArray *allKeys = headers.allKeys;
|
||||
for (NSString *key in allKeys) {
|
||||
[request setValue:headers[key] forHTTPHeaderField:key];
|
||||
}
|
||||
|
||||
if (_useCookies && request.URL) {
|
||||
// Cokies填充
|
||||
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[request.URL absoluteURL]];
|
||||
cookies = QCloudFuseAndUpdateCookiesArray(data.cookies, cookies);
|
||||
NSDictionary *cookiesInfos = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
|
||||
[request setAllHTTPHeaderFields:cookiesInfos];
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// QCloudResponseSerializer.h
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/25.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
typedef id (^QCloudResponseSerializerBlock)(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error);
|
||||
|
||||
@interface QCloudResponseSerializer : NSObject
|
||||
@property (nonatomic, assign) BOOL waitForBodyData;
|
||||
@property (nonatomic, strong) NSArray<QCloudResponseSerializerBlock> *serializerBlocks;
|
||||
- (id)decodeWithWithResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error;
|
||||
@end
|
||||
|
||||
FOUNDATION_EXTERN QCloudResponseSerializerBlock QCloudAcceptRespnseCodeBlock(NSSet *acceptCode, Class errorModel);
|
||||
FOUNDATION_EXTERN QCloudResponseSerializerBlock QCloudResponseXMLSerializerBlock;
|
||||
FOUNDATION_EXTERN QCloudResponseSerializerBlock QCloudResponseJSONSerilizerBlock;
|
||||
FOUNDATION_EXTERN QCloudResponseSerializerBlock QCloudResponseAppendHeadersSerializerBlock;
|
||||
FOUNDATION_EXTERN QCloudResponseSerializerBlock QCloudResponseDataAppendHeadersSerializerBlock;
|
||||
193
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudResponseSerializer.m
generated
Normal file
193
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudResponseSerializer.m
generated
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// QCloudResponseSerializer.m
|
||||
// QCloudNetworking
|
||||
//
|
||||
// Created by tencent on 15/9/25.
|
||||
// Copyright © 2015年 QCloudTernimalLab. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudResponseSerializer.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "NSError+QCloudNetworking.h"
|
||||
#import "QCloudObjectModel.h"
|
||||
#import "QCloudXMLDictionary.h"
|
||||
|
||||
typedef id (^QCloudResponseSerializerBlock)(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error);
|
||||
|
||||
QCloudResponseSerializerBlock QCloudResponseXMLSerializerBlock = ^(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error) {
|
||||
|
||||
if (![inputData isKindOfClass:[NSData class]]) {
|
||||
if (NULL != error) {
|
||||
*error = [NSError qcloud_errorWithCode:QCloudNetworkErrorCodeResponseDataTypeInvalid
|
||||
message:[NSString stringWithFormat:@"ServerError:XML解析器读入的数据不是NSData"]];
|
||||
}
|
||||
return (id)nil;
|
||||
}
|
||||
if ([(NSData *)inputData length] == 0) {
|
||||
NSDictionary *emptyDictionary = [[NSDictionary alloc] init];
|
||||
return (id)emptyDictionary;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#endif
|
||||
QCloudXMLDictionaryParser *parser = [QCloudXMLDictionaryParser new];
|
||||
|
||||
NSDictionary *output = [parser dictionaryWithData:inputData];
|
||||
|
||||
if (!output) {
|
||||
if (NULL != error) {
|
||||
*error = [NSError qcloud_errorWithCode:QCloudNetworkErrorCodeResponseDataTypeInvalid
|
||||
message:[NSString stringWithFormat:@"ServerError:尝试解析XML类型数据出错:\n%@",
|
||||
[[NSString alloc] initWithData:inputData
|
||||
encoding:NSUTF8StringEncoding]]];
|
||||
}
|
||||
return (id)nil;
|
||||
}
|
||||
if (output[@"Code"] && [output[@"__name"] isEqualToString:@"Error"]) {
|
||||
*error = [NSError qcloud_errorWithCode:500
|
||||
message:[NSString stringWithFormat:output[@"Code"], [[NSString alloc] initWithData:inputData
|
||||
encoding:NSUTF8StringEncoding]]];
|
||||
return (id)nil;
|
||||
}
|
||||
QCloudLogDebugR(@"HTTP",@"原始数据:%@", output);
|
||||
return (id)output;
|
||||
};
|
||||
|
||||
QCloudResponseSerializerBlock QCloudResponseAppendHeadersSerializerBlock
|
||||
= ^(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error) {
|
||||
NSMutableDictionary *allDatas = [NSMutableDictionary new];
|
||||
if ([inputData isKindOfClass:[NSDictionary class]]) {
|
||||
[allDatas addEntriesFromDictionary:(NSDictionary *)inputData];
|
||||
}
|
||||
[allDatas addEntriesFromDictionary:response.allHeaderFields];
|
||||
|
||||
return (id)allDatas;
|
||||
};
|
||||
|
||||
QCloudResponseSerializerBlock QCloudResponseDataAppendHeadersSerializerBlock
|
||||
= ^(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error) {
|
||||
NSMutableDictionary *allDatas = [NSMutableDictionary new];
|
||||
if ([inputData isKindOfClass:[NSDictionary class]]) {
|
||||
[allDatas addEntriesFromDictionary:(NSDictionary *)inputData];
|
||||
} else {
|
||||
if (inputData != nil) {
|
||||
[allDatas setObject:inputData forKey:@"data"];
|
||||
}
|
||||
}
|
||||
[allDatas addEntriesFromDictionary:response.allHeaderFields];
|
||||
|
||||
return (id)allDatas;
|
||||
};
|
||||
|
||||
QCloudResponseSerializerBlock QCloudAcceptRespnseCodeBlock(NSSet *acceptCode, Class errorModel) {
|
||||
return ^(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error) {
|
||||
void (^LoadDefaultError)(void) = ^() {
|
||||
NSString *errorMessage = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
|
||||
errorMessage = errorMessage ?: [NSString stringWithFormat:@"不接受该HTTP StatusCode %ld", (long)response.statusCode];
|
||||
if (error != NULL) {
|
||||
*error = [NSError qcloud_errorWithCode:(int)response.statusCode message:errorMessage];
|
||||
}
|
||||
};
|
||||
if ([acceptCode containsObject:@(response.statusCode)]) {
|
||||
return inputData;
|
||||
} else {
|
||||
NSString *contentType = [response.allHeaderFields objectForKey:@"Content-Type"];
|
||||
NSDictionary *userInfo = nil;
|
||||
if (contentType) {
|
||||
if ([contentType.lowercaseString containsString:@"application/json"]) {
|
||||
NSError *localError = nil;
|
||||
NSDictionary *map = [NSJSONSerialization JSONObjectWithData:inputData options:0 error:&localError];
|
||||
if (localError) {
|
||||
LoadDefaultError();
|
||||
} else {
|
||||
userInfo = map;
|
||||
}
|
||||
} else if ([contentType.lowercaseString containsString:@"application/xml"]) {
|
||||
QCloudXMLDictionaryParser *parser = [QCloudXMLDictionaryParser new];
|
||||
NSDictionary *output = [parser dictionaryWithData:inputData];
|
||||
if (output) {
|
||||
userInfo = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userInfo) {
|
||||
if (!errorModel) {
|
||||
if (error != NULL) {
|
||||
*error = [NSError errorWithDomain:kQCloudNetworkDomain code:response.statusCode userInfo:userInfo];
|
||||
}
|
||||
return (id)nil;
|
||||
}
|
||||
if ([errorModel respondsToSelector:@selector(toError:)]) {
|
||||
if (error != NULL) {
|
||||
*error = [errorModel toError:userInfo];
|
||||
}
|
||||
} else {
|
||||
LoadDefaultError();
|
||||
}
|
||||
}
|
||||
if ((error != NULL) && !(*error)) {
|
||||
LoadDefaultError();
|
||||
}
|
||||
return (id)nil;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QCloudResponseSerializerBlock QCloudResponseJSONSerilizerBlock = ^(NSHTTPURLResponse *response, id inputData, NSError *__autoreleasing *error) {
|
||||
if (![inputData isKindOfClass:[NSData class]]) {
|
||||
if (error != NULL) {
|
||||
*error = [NSError errorWithDomain:@"com.tencent.networking"
|
||||
code:-1404
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"数据非法,请传入合法数据" }];
|
||||
}
|
||||
return (id)nil;
|
||||
}
|
||||
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:inputData options:0 error:error];
|
||||
if (*error || !jsonObject) {
|
||||
NSString *str = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
|
||||
QCloudLogErrorE(@"HTTP",@"response data is %@", str);
|
||||
return (id)nil;
|
||||
}
|
||||
QCloudLogDebugR(@"HTTP",@"GET JSON : \n %@", jsonObject);
|
||||
return (id)(jsonObject);
|
||||
};
|
||||
|
||||
@interface QCloudResponseSerializer () {
|
||||
NSMutableArray *_serializerBlocks;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation QCloudResponseSerializer
|
||||
|
||||
- (void)__responseCommonInit {
|
||||
_serializerBlocks = [NSMutableArray new];
|
||||
[_serializerBlocks addObject:QCloudAcceptRespnseCodeBlock([NSSet setWithArray:@[ @(200) ]], nil)];
|
||||
[_serializerBlocks addObject:QCloudResponseJSONSerilizerBlock];
|
||||
_waitForBodyData = YES;
|
||||
}
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self __responseCommonInit];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)decodeWithWithResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error {
|
||||
NSError *localError;
|
||||
id output = data;
|
||||
for (QCloudResponseSerializerBlock block in _serializerBlocks) {
|
||||
output = block(response, output, &localError);
|
||||
if (localError) {
|
||||
if (error != NULL) {
|
||||
*error = localError;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
@end
|
||||
11
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudURLHelper.h
generated
Normal file
11
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudURLHelper.h
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// QCloudURLHelper.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/9/7.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXTERN NSString *QCloudPercentEscapedStringFromString(NSString *string);
|
||||
42
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudURLHelper.m
generated
Normal file
42
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/CoreRequest/Serializer/QCloudURLHelper.m
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// QCloudURLHelper.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/9/7.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudURLHelper.h"
|
||||
|
||||
NSString *QCloudPercentEscapedStringFromString(NSString *string) {
|
||||
static NSString *const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
||||
static NSString *const kAFCharactersSubDelimitersToEncode = @"?!$&'()*+,;=";
|
||||
|
||||
NSMutableCharacterSet *allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
|
||||
[allowedCharacterSet
|
||||
removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
|
||||
|
||||
// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
|
||||
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
|
||||
|
||||
static NSUInteger const batchSize = 50;
|
||||
|
||||
NSUInteger index = 0;
|
||||
NSMutableString *escaped = @"".mutableCopy;
|
||||
|
||||
while (index < string.length) {
|
||||
NSUInteger length = MIN(string.length - index, batchSize);
|
||||
NSRange range = NSMakeRange(index, length);
|
||||
|
||||
// To avoid breaking up character sequences such as 👴🏻👮🏽
|
||||
range = [string rangeOfComposedCharacterSequencesForRange:range];
|
||||
|
||||
NSString *substring = [string substringWithRange:range];
|
||||
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
|
||||
[escaped appendString:encoded];
|
||||
|
||||
index += range.length;
|
||||
}
|
||||
|
||||
return escaped;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// QCloudXMLDictionary.h
|
||||
//
|
||||
// Version 1.4.1
|
||||
//
|
||||
// Created by Nick Lockwood on 15/11/2010.
|
||||
// Copyright 2010 Charcoal Design. All rights reserved.
|
||||
//
|
||||
// Get the latest version of QCloudXMLDictionary from here:
|
||||
//
|
||||
// https://github.com/nicklockwood/QCloudXMLDictionary
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, QCloudXMLDictionaryAttributesMode) {
|
||||
QCloudXMLDictionaryAttributesModePrefixed = 0, // default
|
||||
QCloudXMLDictionaryAttributesModeDictionary,
|
||||
QCloudXMLDictionaryAttributesModeUnprefixed,
|
||||
QCloudXMLDictionaryAttributesModeDiscard
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, QCloudXMLDictionaryNodeNameMode) {
|
||||
QCloudXMLDictionaryNodeNameModeRootOnly = 0, // default
|
||||
QCloudXMLDictionaryNodeNameModeAlways,
|
||||
QCloudXMLDictionaryNodeNameModeNever
|
||||
};
|
||||
|
||||
static NSString *const QCloudXMLDictionaryAttributesKey = @"__attributes";
|
||||
static NSString *const QCloudXMLDictionaryCommentsKey = @"__comments";
|
||||
static NSString *const QCloudXMLDictionaryTextKey = @"__text";
|
||||
static NSString *const QCloudXMLDictionaryNodeNameKey = @"__name";
|
||||
static NSString *const QCloudXMLDictionaryAttributePrefix = @"_";
|
||||
|
||||
@interface QCloudXMLDictionaryParser : NSObject <NSCopying>
|
||||
|
||||
+ (QCloudXMLDictionaryParser *)sharedInstance;
|
||||
|
||||
@property (nonatomic, assign) BOOL collapseTextNodes; // defaults to YES
|
||||
@property (nonatomic, assign) BOOL stripEmptyNodes; // defaults to YES
|
||||
@property (nonatomic, assign) BOOL trimWhiteSpace; // defaults to YES
|
||||
@property (nonatomic, assign) BOOL alwaysUseArrays; // defaults to NO
|
||||
@property (nonatomic, assign) BOOL preserveComments; // defaults to NO
|
||||
@property (nonatomic, assign) BOOL wrapRootNode; // defaults to NO
|
||||
|
||||
@property (nonatomic, assign) QCloudXMLDictionaryAttributesMode attributesMode;
|
||||
@property (nonatomic, assign) QCloudXMLDictionaryNodeNameMode nodeNameMode;
|
||||
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryWithParser:(NSXMLParser *)parser;
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryWithData:(NSData *)data;
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryWithString:(NSString *)string;
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryWithFile:(NSString *)path;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSDictionary (QCloudXMLDictionary)
|
||||
|
||||
+ (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLParser:(NSXMLParser *)parser;
|
||||
+ (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLData:(NSData *)data;
|
||||
+ (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLString:(NSString *)string;
|
||||
+ (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLFile:(NSString *)path;
|
||||
|
||||
@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, NSString *> *qcxml_attributes;
|
||||
@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, id> *qcxml_childNodes;
|
||||
@property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *qcxml_comments;
|
||||
@property (nonatomic, readonly, copy, nullable) NSString *qcxml_nodeName;
|
||||
@property (nonatomic, readonly, copy, nullable) NSString *qcxml_innerText;
|
||||
@property (nonatomic, readonly, copy) NSString *qcxml_innerXML;
|
||||
@property (nonatomic, readonly, copy) NSString *qcxml_XMLString;
|
||||
|
||||
- (nullable NSArray *)qcxml_arrayValueForKeyPath:(NSString *)keyPath;
|
||||
- (nullable NSString *)qcxml_stringValueForKeyPath:(NSString *)keyPath;
|
||||
- (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryValueForKeyPath:(NSString *)keyPath;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSString (QCloudXMLDictionary)
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *QCXMLEncodedString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
@@ -0,0 +1,450 @@
|
||||
//
|
||||
// QCloudXMLDictionary.m
|
||||
//
|
||||
// Version 1.4.1
|
||||
//
|
||||
// Created by Nick Lockwood on 15/11/2010.
|
||||
// Copyright 2010 Charcoal Design. All rights reserved.
|
||||
//
|
||||
// Get the latest version of QCloudXMLDictionary from here:
|
||||
//
|
||||
// https://github.com/nicklockwood/QCloudXMLDictionary
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#import "QCloudXMLDictionary.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
|
||||
#pragma GCC diagnostic ignored "-Wpartial-availability"
|
||||
#pragma GCC diagnostic ignored "-Wdirect-ivar-access"
|
||||
#pragma GCC diagnostic ignored "-Wformat-non-iso"
|
||||
#pragma GCC diagnostic ignored "-Wgnu"
|
||||
|
||||
#import <Availability.h>
|
||||
#if !__has_feature(objc_arc)
|
||||
#error This class requires automatic reference counting
|
||||
#endif
|
||||
|
||||
@interface QCloudXMLDictionaryParser () <NSXMLParserDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *root;
|
||||
@property (nonatomic, strong) NSMutableArray *stack;
|
||||
@property (nonatomic, strong) NSMutableString *text;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudXMLDictionaryParser
|
||||
|
||||
+ (QCloudXMLDictionaryParser *)sharedInstance {
|
||||
static dispatch_once_t once;
|
||||
static QCloudXMLDictionaryParser *sharedInstance;
|
||||
dispatch_once(&once, ^{
|
||||
sharedInstance = [[QCloudXMLDictionaryParser alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_collapseTextNodes = YES;
|
||||
_stripEmptyNodes = YES;
|
||||
_trimWhiteSpace = YES;
|
||||
_alwaysUseArrays = NO;
|
||||
_preserveComments = NO;
|
||||
_wrapRootNode = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
QCloudXMLDictionaryParser *copy = [[[self class] allocWithZone:zone] init];
|
||||
copy.collapseTextNodes = _collapseTextNodes;
|
||||
copy.stripEmptyNodes = _stripEmptyNodes;
|
||||
copy.trimWhiteSpace = _trimWhiteSpace;
|
||||
copy.alwaysUseArrays = _alwaysUseArrays;
|
||||
copy.preserveComments = _preserveComments;
|
||||
copy.attributesMode = _attributesMode;
|
||||
copy.nodeNameMode = _nodeNameMode;
|
||||
copy.wrapRootNode = _wrapRootNode;
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)dictionaryWithParser:(NSXMLParser *)parser {
|
||||
parser.delegate = self;
|
||||
[parser parse];
|
||||
id result = _root;
|
||||
_root = nil;
|
||||
_stack = nil;
|
||||
_text = nil;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)dictionaryWithData:(NSData *)data {
|
||||
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
|
||||
[parser setShouldResolveExternalEntities:NO];
|
||||
return [self dictionaryWithParser:parser];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)dictionaryWithString:(NSString *)string {
|
||||
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self dictionaryWithData:data];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)dictionaryWithFile:(NSString *)path {
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
return [self dictionaryWithData:data];
|
||||
}
|
||||
|
||||
+ (NSString *)XMLStringForNode:(id)node withNodeName:(NSString *)nodeName {
|
||||
if ([node isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray<NSString *> *nodes = [NSMutableArray arrayWithCapacity:[node count]];
|
||||
for (id individualNode in node) {
|
||||
[nodes addObject:[self XMLStringForNode:individualNode withNodeName:nodeName]];
|
||||
}
|
||||
return [nodes componentsJoinedByString:@"\n"];
|
||||
} else if ([node isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary<NSString *, NSString *> *attributes = [(NSDictionary *)node qcxml_attributes];
|
||||
NSMutableString *attributeString = [NSMutableString string];
|
||||
[attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
|
||||
[attributeString appendFormat:@" %@=\"%@\"", key.description.QCXMLEncodedString, value.description.QCXMLEncodedString];
|
||||
}];
|
||||
|
||||
NSString *innerXML = [node qcxml_innerXML];
|
||||
if (innerXML.length) {
|
||||
return [NSString stringWithFormat:@"<%1$@%2$@>%3$@</%1$@>", nodeName, attributeString, innerXML];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"<%@%@/>", nodeName, attributeString];
|
||||
}
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"<%1$@>%2$@</%1$@>", nodeName, [node description].QCXMLEncodedString];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)endText {
|
||||
if (_trimWhiteSpace) {
|
||||
_text = [[_text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
|
||||
}
|
||||
if (_text.length) {
|
||||
NSMutableDictionary *top = _stack.lastObject;
|
||||
id existing = top[QCloudXMLDictionaryTextKey];
|
||||
if ([existing isKindOfClass:[NSArray class]]) {
|
||||
[existing addObject:_text];
|
||||
} else if (existing) {
|
||||
top[QCloudXMLDictionaryTextKey] = [@[ existing, _text ] mutableCopy];
|
||||
} else {
|
||||
top[QCloudXMLDictionaryTextKey] = _text;
|
||||
}
|
||||
}
|
||||
_text = nil;
|
||||
}
|
||||
|
||||
- (void)addText:(NSString *)text {
|
||||
if (!_text) {
|
||||
_text = [NSMutableString stringWithString:text];
|
||||
} else {
|
||||
[_text appendString:text];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)parser:(__unused NSXMLParser *)parser
|
||||
didStartElement:(NSString *)elementName
|
||||
namespaceURI:(__unused NSString *)namespaceURI
|
||||
qualifiedName:(__unused NSString *)qName
|
||||
attributes:(NSDictionary *)attributeDict {
|
||||
if ([elementName isEqualToString:@"CommonPrefixes"] || [elementName isEqualToString:@"Key"]) {
|
||||
self.trimWhiteSpace = NO;
|
||||
}
|
||||
[self endText];
|
||||
|
||||
NSMutableDictionary<NSString *, id> *node = [NSMutableDictionary dictionary];
|
||||
switch (_nodeNameMode) {
|
||||
case QCloudXMLDictionaryNodeNameModeRootOnly: {
|
||||
if (!_root) {
|
||||
node[QCloudXMLDictionaryNodeNameKey] = elementName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QCloudXMLDictionaryNodeNameModeAlways: {
|
||||
node[QCloudXMLDictionaryNodeNameKey] = elementName;
|
||||
break;
|
||||
}
|
||||
case QCloudXMLDictionaryNodeNameModeNever: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attributeDict.count) {
|
||||
switch (_attributesMode) {
|
||||
case QCloudXMLDictionaryAttributesModePrefixed: {
|
||||
for (NSString *key in attributeDict) {
|
||||
node[[QCloudXMLDictionaryAttributePrefix stringByAppendingString:key]] = attributeDict[key];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QCloudXMLDictionaryAttributesModeDictionary: {
|
||||
node[QCloudXMLDictionaryAttributesKey] = attributeDict;
|
||||
break;
|
||||
}
|
||||
case QCloudXMLDictionaryAttributesModeUnprefixed: {
|
||||
[node addEntriesFromDictionary:attributeDict];
|
||||
break;
|
||||
}
|
||||
case QCloudXMLDictionaryAttributesModeDiscard: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_root) {
|
||||
_root = node;
|
||||
_stack = [NSMutableArray arrayWithObject:node];
|
||||
if (_wrapRootNode) {
|
||||
_root = [NSMutableDictionary dictionaryWithObject:_root forKey:elementName];
|
||||
[_stack insertObject:_root atIndex:0];
|
||||
}
|
||||
} else {
|
||||
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
|
||||
id existing = top[elementName];
|
||||
if ([existing isKindOfClass:[NSArray class]]) {
|
||||
[(NSMutableArray *)existing addObject:node];
|
||||
} else if (existing) {
|
||||
top[elementName] = [@[ existing, node ] mutableCopy];
|
||||
} else if (_alwaysUseArrays) {
|
||||
top[elementName] = [NSMutableArray arrayWithObject:node];
|
||||
} else {
|
||||
top[elementName] = node;
|
||||
}
|
||||
[_stack addObject:node];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)nameForNode:(NSDictionary<NSString *, id> *)node inDictionary:(NSDictionary<NSString *, id> *)dict {
|
||||
if (node.qcxml_nodeName) {
|
||||
return node.qcxml_nodeName;
|
||||
} else {
|
||||
for (NSString *name in dict) {
|
||||
id object = dict[name];
|
||||
if (object == node) {
|
||||
return name;
|
||||
} else if ([object isKindOfClass:[NSArray class]] && [(NSArray *)object containsObject:node]) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parser:(__unused NSXMLParser *)parser
|
||||
didEndElement:(__unused NSString *)elementName
|
||||
namespaceURI:(__unused NSString *)namespaceURI
|
||||
qualifiedName:(__unused NSString *)qName {
|
||||
[self endText];
|
||||
|
||||
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
|
||||
[_stack removeLastObject];
|
||||
|
||||
if (!top.qcxml_attributes && !top.qcxml_childNodes && !top.qcxml_comments) {
|
||||
NSMutableDictionary<NSString *, id> *newTop = _stack.lastObject;
|
||||
NSString *nodeName = [self nameForNode:top inDictionary:newTop];
|
||||
if (nodeName) {
|
||||
id parentNode = newTop[nodeName];
|
||||
NSString *innerText = top.qcxml_innerText;
|
||||
if (innerText && _collapseTextNodes) {
|
||||
if ([parentNode isKindOfClass:[NSArray class]]) {
|
||||
parentNode[[parentNode count] - 1] = innerText;
|
||||
} else {
|
||||
newTop[nodeName] = innerText;
|
||||
}
|
||||
} else if (!innerText) {
|
||||
if (_stripEmptyNodes) {
|
||||
if ([parentNode isKindOfClass:[NSArray class]]) {
|
||||
[(NSMutableArray *)parentNode removeLastObject];
|
||||
} else {
|
||||
[newTop removeObjectForKey:nodeName];
|
||||
}
|
||||
} else if (!_collapseTextNodes) {
|
||||
top[QCloudXMLDictionaryTextKey] = @"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string {
|
||||
[self addText:string];
|
||||
}
|
||||
|
||||
- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {
|
||||
[self addText:[[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
- (void)parser:(__unused NSXMLParser *)parser foundComment:(NSString *)comment {
|
||||
if (_preserveComments) {
|
||||
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
|
||||
NSMutableArray<NSString *> *comments = top[QCloudXMLDictionaryCommentsKey];
|
||||
if (!comments) {
|
||||
comments = [@[ comment ] mutableCopy];
|
||||
top[QCloudXMLDictionaryCommentsKey] = comments;
|
||||
} else {
|
||||
[comments addObject:comment];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSDictionary (QCloudXMLDictionary)
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLParser:(NSXMLParser *)parser {
|
||||
return [[[QCloudXMLDictionaryParser sharedInstance] copy] dictionaryWithParser:parser];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLData:(NSData *)data {
|
||||
return [[[QCloudXMLDictionaryParser sharedInstance] copy] dictionaryWithData:data];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLString:(NSString *)string {
|
||||
return [[[QCloudXMLDictionaryParser sharedInstance] copy] dictionaryWithString:string];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)qcxml_dictionaryWithXMLFile:(NSString *)path {
|
||||
return [[[QCloudXMLDictionaryParser sharedInstance] copy] dictionaryWithFile:path];
|
||||
}
|
||||
|
||||
- (nullable NSDictionary<NSString *, NSString *> *)qcxml_attributes {
|
||||
NSDictionary<NSString *, NSString *> *attributes = self[QCloudXMLDictionaryAttributesKey];
|
||||
if (attributes) {
|
||||
return attributes.count ? attributes : nil;
|
||||
} else {
|
||||
NSMutableDictionary<NSString *, id> *filteredDict = [NSMutableDictionary dictionaryWithDictionary:self];
|
||||
[filteredDict removeObjectsForKeys:@[ QCloudXMLDictionaryCommentsKey, QCloudXMLDictionaryTextKey, QCloudXMLDictionaryNodeNameKey ]];
|
||||
for (NSString *key in filteredDict.allKeys) {
|
||||
[filteredDict removeObjectForKey:key];
|
||||
if ([key hasPrefix:QCloudXMLDictionaryAttributePrefix]) {
|
||||
filteredDict[[key substringFromIndex:QCloudXMLDictionaryAttributePrefix.length]] = self[key];
|
||||
}
|
||||
}
|
||||
return filteredDict.count ? filteredDict : nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (nullable NSDictionary *)qcxml_childNodes {
|
||||
NSMutableDictionary *filteredDict = [self mutableCopy];
|
||||
[filteredDict removeObjectsForKeys:@[
|
||||
QCloudXMLDictionaryAttributesKey, QCloudXMLDictionaryCommentsKey, QCloudXMLDictionaryTextKey, QCloudXMLDictionaryNodeNameKey
|
||||
]];
|
||||
for (NSString *key in filteredDict.allKeys) {
|
||||
if ([key hasPrefix:QCloudXMLDictionaryAttributePrefix]) {
|
||||
[filteredDict removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
return filteredDict.count ? filteredDict : nil;
|
||||
}
|
||||
|
||||
- (nullable NSArray *)qcxml_comments {
|
||||
return self[QCloudXMLDictionaryCommentsKey];
|
||||
}
|
||||
|
||||
- (nullable NSString *)qcxml_nodeName {
|
||||
return self[QCloudXMLDictionaryNodeNameKey];
|
||||
}
|
||||
|
||||
- (id)qcxml_innerText {
|
||||
id text = self[QCloudXMLDictionaryTextKey];
|
||||
if ([text isKindOfClass:[NSArray class]]) {
|
||||
return [text componentsJoinedByString:@"\n"];
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)qcxml_innerXML {
|
||||
NSMutableArray *nodes = [NSMutableArray array];
|
||||
|
||||
for (NSString *comment in [self qcxml_comments]) {
|
||||
[nodes addObject:[NSString stringWithFormat:@"<!--%@-->", [comment QCXMLEncodedString]]];
|
||||
}
|
||||
|
||||
NSDictionary *childNodes = [self qcxml_childNodes];
|
||||
for (NSString *key in childNodes) {
|
||||
[nodes addObject:[QCloudXMLDictionaryParser XMLStringForNode:childNodes[key] withNodeName:key]];
|
||||
}
|
||||
|
||||
NSString *text = [self qcxml_innerText];
|
||||
if (text) {
|
||||
[nodes addObject:[text QCXMLEncodedString]];
|
||||
}
|
||||
|
||||
return [nodes componentsJoinedByString:@"\n"];
|
||||
}
|
||||
|
||||
- (NSString *)qcxml_XMLString {
|
||||
if (self.count == 1 && ![self qcxml_nodeName]) {
|
||||
// ignore outermost dictionary
|
||||
return [self qcxml_innerXML];
|
||||
} else {
|
||||
return [QCloudXMLDictionaryParser XMLStringForNode:self withNodeName:[self qcxml_nodeName] ?: @"root"];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSArray *)qcxml_arrayValueForKeyPath:(NSString *)keyPath {
|
||||
id value = [self valueForKeyPath:keyPath];
|
||||
if (value && ![value isKindOfClass:[NSArray class]]) {
|
||||
return @[ value ];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (nullable NSString *)qcxml_stringValueForKeyPath:(NSString *)keyPath {
|
||||
id value = [self valueForKeyPath:keyPath];
|
||||
if ([value isKindOfClass:[NSArray class]]) {
|
||||
value = ((NSArray *)value).firstObject;
|
||||
}
|
||||
if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
return [(NSDictionary *)value qcxml_innerText];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (nullable NSDictionary<NSString *, id> *)qcxml_dictionaryValueForKeyPath:(NSString *)keyPath {
|
||||
id value = [self valueForKeyPath:keyPath];
|
||||
if ([value isKindOfClass:[NSArray class]]) {
|
||||
value = [value count] ? value[0] : nil;
|
||||
}
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return @{ QCloudXMLDictionaryTextKey : value };
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (QCloudXMLDictionary)
|
||||
|
||||
- (NSString *)QCXMLEncodedString {
|
||||
return [[[[[self stringByReplacingOccurrencesOfString:@"&" withString:@"&"] stringByReplacingOccurrencesOfString:@"<" withString:@"<"]
|
||||
stringByReplacingOccurrencesOfString:@">"
|
||||
withString:@">"] stringByReplacingOccurrencesOfString:@"\""
|
||||
withString:@"""] stringByReplacingOccurrencesOfString:@"\'"
|
||||
withString:@"'"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,148 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* XMLWriter: An XML stream writer for iOS.
|
||||
* This file is part of the XSWI library - https://skjolber.github.io/xswi
|
||||
*
|
||||
* Copyright (C) 2010 by Thomas Rørvik Skjølberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// xml stream writer
|
||||
@protocol QCloudXMLStreamWriter
|
||||
|
||||
- (void)writeStartDocument;
|
||||
- (void)writeStartDocumentWithVersion:(NSString *)version;
|
||||
- (void)writeStartDocumentWithEncodingAndVersion:(NSString *)encoding version:(NSString *)version;
|
||||
|
||||
- (void)writeStartElement:(NSString *)localName;
|
||||
|
||||
- (void)writeEndElement; // automatic end element (mirrors previous start element at the same level)
|
||||
- (void)writeEndElement:(NSString *)localName;
|
||||
|
||||
- (void)writeEmptyElement:(NSString *)localName;
|
||||
|
||||
- (void)writeEndDocument; // write any remaining end elements
|
||||
|
||||
- (void)writeAttribute:(NSString *)localName value:(NSString *)value;
|
||||
|
||||
- (void)writeCharacters:(NSString *)text;
|
||||
- (void)writeComment:(NSString *)comment;
|
||||
- (void)writeProcessingInstruction:(NSString *)target data:(NSString *)data;
|
||||
- (void)writeCData:(NSString *)cdata;
|
||||
|
||||
// return the written xml string buffer
|
||||
- (NSMutableString *)toString;
|
||||
// return the written xml as data, set to the encoding used in the writeStartDocumentWithEncodingAndVersion method (UTF-8 per default)
|
||||
- (NSData *)toData;
|
||||
|
||||
// flush the buffers, if any
|
||||
- (void)flush;
|
||||
// close the writer and buffers, if any
|
||||
- (void)close;
|
||||
|
||||
- (void)setPrettyPrinting:(NSString *)indentation withLineBreak:(NSString *)lineBreak;
|
||||
|
||||
@end
|
||||
|
||||
// xml stream writer with namespace support
|
||||
@protocol NSXMLStreamWriter <QCloudXMLStreamWriter>
|
||||
|
||||
- (void)writeStartElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName;
|
||||
- (void)writeEndElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName;
|
||||
- (void)writeEmptyElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName;
|
||||
|
||||
- (void)writeAttributeWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName value:(NSString *)value;
|
||||
|
||||
// set a namespace and prefix
|
||||
- (void)setPrefix:(NSString *)prefix namespaceURI:(NSString *)namespaceURI;
|
||||
// write (and set) a namespace and prefix
|
||||
- (void)writeNamespace:(NSString *)prefix namespaceURI:(NSString *)namespaceURI;
|
||||
|
||||
// set the default namespace (empty prefix)
|
||||
- (void)setDefaultNamespace:(NSString *)namespaceURI;
|
||||
// write (and set) the default namespace
|
||||
- (void)writeDefaultNamespace:(NSString *)namespaceURI;
|
||||
|
||||
- (NSString *)getPrefix:(NSString *)namespaceURI;
|
||||
- (NSString *)getNamespaceURI:(NSString *)prefix;
|
||||
|
||||
@end
|
||||
|
||||
@interface QCloudXMLWriter : NSObject <NSXMLStreamWriter> {
|
||||
// the current output buffer
|
||||
NSMutableString *writer;
|
||||
|
||||
// the target encoding
|
||||
NSString *encoding;
|
||||
|
||||
// the number current levels
|
||||
int level;
|
||||
// is the element open, i.e. the end bracket has not been written yet
|
||||
BOOL openElement;
|
||||
// does the element contain characters, cdata, comments
|
||||
BOOL emptyElement;
|
||||
|
||||
// the element stack. one per element level
|
||||
NSMutableArray *elementLocalNames;
|
||||
NSMutableArray *elementNamespaceURIs;
|
||||
|
||||
// the namespace array. zero or more namespace attributes can be defined per element level
|
||||
NSMutableArray *namespaceURIs;
|
||||
// the namespace count. one per element level
|
||||
NSMutableArray *namespaceCounts;
|
||||
// the namespaces which have been written to the stream
|
||||
NSMutableArray *namespaceWritten;
|
||||
|
||||
// mapping of namespace URI to prefix and visa versa. Corresponds in size to the namespaceURIs array.
|
||||
NSMutableDictionary *namespaceURIPrefixMap;
|
||||
NSMutableDictionary *prefixNamespaceURIMap;
|
||||
|
||||
// tag indentation
|
||||
NSString *indentation;
|
||||
// line break
|
||||
NSString *lineBreak;
|
||||
|
||||
// if true, then write elements without children as <start /> instead of <start></start>
|
||||
BOOL automaticEmptyElements;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain, readwrite) NSString *indentation;
|
||||
@property (nonatomic, retain, readwrite) NSString *lineBreak;
|
||||
@property (nonatomic, assign, readwrite) BOOL automaticEmptyElements;
|
||||
@property (nonatomic, readonly) int level;
|
||||
|
||||
// helpful for formatting, special needs
|
||||
// write linebreak, if any
|
||||
- (void)writeLinebreak;
|
||||
// write indentation, if any
|
||||
- (void)writeIndentation;
|
||||
// write end of start element, so that the start tag is complete
|
||||
- (void)writeCloseStartElement;
|
||||
|
||||
// write any outstanding namespace declaration attributes in a start element
|
||||
- (void)writeNamespaceAttributes;
|
||||
// write escaped text to the stream
|
||||
- (void)writeEscape:(NSString *)value;
|
||||
// wrote unescaped text to the stream
|
||||
- (void)write:(NSString *)value;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,760 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* XMLWriter: An XML stream writer for iOS.
|
||||
* This file is part of the XSWI library - https://skjolber.github.io/xswi
|
||||
*
|
||||
* Copyright (C) 2010 by Thomas Rørvik Skjølberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#import "QCloudXMLWriter.h"
|
||||
|
||||
#define NSBOOL(_X_) ((_X_) ? (id)kCFBooleanTrue : (id)kCFBooleanFalse)
|
||||
|
||||
@interface QCloudXMLWriter (UtilityMethods)
|
||||
// methods for internal use only
|
||||
// pop the namespace stack, removing any namespaces which become out-of-scope
|
||||
- (void)popNamespaceStack;
|
||||
// push the namespace stack, denoting the namespaces whihch are in-scope
|
||||
- (void)pushNamespaceStack;
|
||||
|
||||
// add namespace and local name to the top of the element stack
|
||||
- (void)pushElementStack:(NSString *)namespaceURI localName:(NSString *)localName;
|
||||
// remove the top member of the element stack
|
||||
- (void)popElementStack;
|
||||
|
||||
// write close element, optionally as empty element
|
||||
- (void)writeCloseElement:(BOOL)empty;
|
||||
// write namespace attribute to stream
|
||||
- (void)writeNamespaceToStream:(NSString *)prefix namespaceURI:(NSString *)namespaceURI;
|
||||
// write a length of text to the stream with escaping
|
||||
- (void)writeEscapeCharacters:(const UniChar *)characters length:(NSUInteger)length;
|
||||
@end
|
||||
|
||||
static NSString *const EMPTY_STRING = @"";
|
||||
static NSString *const XML_NAMESPACE_URI = @"http://www.w3.org/XML/1998/namespace";
|
||||
static NSString *const XML_NAMESPACE_URI_PREFIX = @"xml";
|
||||
static NSString *const XMLNS_NAMESPACE_URI = @"http://www.w3.org/2000/xmlns/";
|
||||
static NSString *const XMLNS_NAMESPACE_URI_PREFIX = @"xmlns";
|
||||
static NSString *const XSI_NAMESPACE_URI = @"http://www.w3.org/2001/XMLSchema/";
|
||||
static NSString *const XSI_NAMESPACE_URI_PREFIX = @"xsi";
|
||||
|
||||
@implementation QCloudXMLWriter
|
||||
|
||||
@synthesize automaticEmptyElements, indentation, lineBreak, level;
|
||||
|
||||
- (QCloudXMLWriter *)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
// intialize variables
|
||||
writer = [[NSMutableString alloc] init];
|
||||
level = 0;
|
||||
openElement = NO;
|
||||
emptyElement = NO;
|
||||
|
||||
elementLocalNames = [[NSMutableArray alloc] init];
|
||||
elementNamespaceURIs = [[NSMutableArray alloc] init];
|
||||
|
||||
namespaceURIs = [[NSMutableArray alloc] init];
|
||||
namespaceCounts = [[NSMutableArray alloc] init];
|
||||
namespaceWritten = [[NSMutableArray alloc] init];
|
||||
|
||||
namespaceURIPrefixMap = [[NSMutableDictionary alloc] init];
|
||||
prefixNamespaceURIMap = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// load default custom behaviour
|
||||
automaticEmptyElements = YES;
|
||||
|
||||
// setup default xml namespaces. assume both are previously known.
|
||||
[namespaceCounts addObject:[NSNumber numberWithInt:2]];
|
||||
[self setPrefix:XML_NAMESPACE_URI_PREFIX namespaceURI:XML_NAMESPACE_URI];
|
||||
[self setPrefix:XMLNS_NAMESPACE_URI_PREFIX namespaceURI:XMLNS_NAMESPACE_URI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)pushNamespaceStack {
|
||||
// step namespace count - add the current namespace count
|
||||
NSNumber *previousCount = [namespaceCounts lastObject];
|
||||
if ([namespaceURIs count] == [previousCount unsignedIntegerValue]) {
|
||||
// the count is still the same
|
||||
[namespaceCounts addObject:previousCount];
|
||||
} else {
|
||||
// the count has changed, save the it
|
||||
NSNumber *count = [NSNumber numberWithInt:(int)[namespaceURIs count]];
|
||||
|
||||
[namespaceCounts addObject:count];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeNamespaceAttributes {
|
||||
if (openElement) {
|
||||
// write namespace attributes in the namespace stack
|
||||
NSNumber *previousCount = [namespaceCounts lastObject];
|
||||
for (NSUInteger i = [previousCount unsignedIntegerValue]; i < [namespaceURIs count]; i++) {
|
||||
// did we already write this namespace?
|
||||
id written = [namespaceWritten objectAtIndex:i];
|
||||
if (written == NSBOOL(NO)) {
|
||||
// write namespace
|
||||
NSString *namespaceURI = [namespaceURIs objectAtIndex:i];
|
||||
NSString *prefix = [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
|
||||
[self writeNamespaceToStream:prefix namespaceURI:namespaceURI];
|
||||
|
||||
[namespaceWritten replaceObjectAtIndex:i withObject:NSBOOL(YES)];
|
||||
} else {
|
||||
// already written namespace
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"No open start element" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)popNamespaceStack {
|
||||
// step namespaces one level down
|
||||
if ([namespaceCounts lastObject] != [namespaceCounts objectAtIndex:([namespaceCounts count] - 2)]) {
|
||||
// remove namespaces which now are out of scope, i.e. between the current and the previus count
|
||||
NSNumber *previousCount = [namespaceCounts lastObject];
|
||||
NSNumber *currentCount = [namespaceCounts objectAtIndex:([namespaceCounts count] - 2)];
|
||||
for (NSUInteger i = [previousCount unsignedIntegerValue] - 1; i >= [currentCount unsignedIntegerValue]; i--) {
|
||||
NSString *removedNamespaceURI = [namespaceURIs objectAtIndex:i];
|
||||
NSString *removedPrefix = [namespaceURIPrefixMap objectForKey:removedNamespaceURI];
|
||||
|
||||
[prefixNamespaceURIMap removeObjectForKey:removedPrefix];
|
||||
[namespaceURIPrefixMap removeObjectForKey:removedNamespaceURI];
|
||||
|
||||
[namespaceURIs removeLastObject];
|
||||
|
||||
[namespaceWritten removeLastObject];
|
||||
}
|
||||
} else {
|
||||
// not necessary to remove any namespaces
|
||||
}
|
||||
[namespaceCounts removeLastObject];
|
||||
}
|
||||
|
||||
- (void)setPrefix:(NSString *)prefix namespaceURI:(NSString *)namespaceURI {
|
||||
if (!namespaceURI) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"Namespace cannot be NULL" userInfo:NULL]);
|
||||
}
|
||||
if (!prefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"Prefix cannot be NULL" userInfo:NULL]);
|
||||
}
|
||||
if ([namespaceURIPrefixMap objectForKey:namespaceURI]) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Name namespace %@ has already been set", namespaceURI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
if ([prefixNamespaceURIMap objectForKey:prefix]) {
|
||||
// raise exception
|
||||
if ([prefix length]) {
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Prefix %@ has already been set", prefix]
|
||||
userInfo:NULL]);
|
||||
} else {
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"Default namespace has already been set" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
// increase the namespaces and add prefix mapping
|
||||
[namespaceURIs addObject:namespaceURI];
|
||||
[namespaceURIPrefixMap setObject:prefix forKey:namespaceURI];
|
||||
[prefixNamespaceURIMap setObject:namespaceURI forKey:prefix];
|
||||
|
||||
if (openElement) { // write the namespace now
|
||||
[self writeNamespaceToStream:prefix namespaceURI:namespaceURI];
|
||||
|
||||
[namespaceWritten addObject:NSBOOL(YES)];
|
||||
} else {
|
||||
// write the namespace as the next start element is closed
|
||||
[namespaceWritten addObject:NSBOOL(NO)];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)getPrefix:(NSString *)namespaceURI {
|
||||
return [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
}
|
||||
|
||||
- (void)pushElementStack:(NSString *)namespaceURI localName:(NSString *)localName {
|
||||
// save for end elements
|
||||
[elementLocalNames addObject:localName];
|
||||
if (namespaceURI) {
|
||||
[elementNamespaceURIs addObject:namespaceURI];
|
||||
} else {
|
||||
[elementNamespaceURIs addObject:EMPTY_STRING];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)popElementStack {
|
||||
// remove element traces
|
||||
[elementNamespaceURIs removeLastObject];
|
||||
[elementLocalNames removeLastObject];
|
||||
}
|
||||
|
||||
- (void)writeStartDocument {
|
||||
[self writeStartDocumentWithEncodingAndVersion:NULL version:NULL];
|
||||
}
|
||||
|
||||
- (void)writeStartDocumentWithVersion:(NSString *)version {
|
||||
[self writeStartDocumentWithEncodingAndVersion:NULL version:version];
|
||||
}
|
||||
|
||||
- (void)writeStartDocumentWithEncodingAndVersion:(NSString *)aEncoding version:(NSString *)version {
|
||||
if ([writer length] != 0) {
|
||||
// raise exception - Starting document which is not empty
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"Document has already been started" userInfo:NULL]);
|
||||
} else {
|
||||
[self write:@"<?xml version=\""];
|
||||
if (version) {
|
||||
[self write:version];
|
||||
} else {
|
||||
// default to 1.0
|
||||
[self write:@"1.0"];
|
||||
}
|
||||
[self write:@"\""];
|
||||
|
||||
if (aEncoding) {
|
||||
[self write:@" encoding=\""];
|
||||
[self write:aEncoding];
|
||||
[self write:@"\""];
|
||||
|
||||
encoding = aEncoding;
|
||||
}
|
||||
[self write:@" ?>"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeEndDocument {
|
||||
while (level > 0) {
|
||||
[self writeEndElement];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeStartElement:(NSString *)localName {
|
||||
[self writeStartElementWithNamespace:NULL localName:localName];
|
||||
}
|
||||
|
||||
- (void)writeCloseStartElement {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
} else {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"No open start element" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeCloseElement:(BOOL)empty {
|
||||
[self writeNamespaceAttributes];
|
||||
[self pushNamespaceStack];
|
||||
|
||||
if (empty) {
|
||||
[self write:@" />"];
|
||||
} else {
|
||||
[self write:@">"];
|
||||
}
|
||||
|
||||
openElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeEndElement:(NSString *)localName {
|
||||
[self writeEndElementWithNamespace:NULL localName:localName];
|
||||
}
|
||||
|
||||
- (void)writeEndElement {
|
||||
if (openElement && automaticEmptyElements) {
|
||||
// go for <START />
|
||||
[self writeCloseElement:YES]; // write empty end element
|
||||
|
||||
[self popNamespaceStack];
|
||||
[self popElementStack];
|
||||
|
||||
emptyElement = YES;
|
||||
openElement = NO;
|
||||
|
||||
level -= 1;
|
||||
} else {
|
||||
NSString *namespaceURI = [elementNamespaceURIs lastObject];
|
||||
NSString *localName = [elementLocalNames lastObject];
|
||||
|
||||
if (namespaceURI == EMPTY_STRING) {
|
||||
[self writeEndElementWithNamespace:NULL localName:localName];
|
||||
} else {
|
||||
[self writeEndElementWithNamespace:namespaceURI localName:localName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeStartElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
|
||||
[self writeLinebreak];
|
||||
[self writeIndentation];
|
||||
|
||||
[self write:@"<"];
|
||||
if (namespaceURI) {
|
||||
NSString *prefix = [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
|
||||
if (!prefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Unknown namespace URI %@", namespaceURI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
if ([prefix length]) {
|
||||
[self write:prefix];
|
||||
[self write:@":"];
|
||||
}
|
||||
}
|
||||
[self write:localName];
|
||||
|
||||
[self pushElementStack:namespaceURI localName:localName];
|
||||
|
||||
openElement = YES;
|
||||
emptyElement = YES;
|
||||
level += 1;
|
||||
}
|
||||
|
||||
- (void)writeEndElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName {
|
||||
if (level <= 0) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"Cannot write more end elements than start elements." userInfo:NULL]);
|
||||
}
|
||||
|
||||
level -= 1;
|
||||
|
||||
if (openElement) {
|
||||
// go for <START><END>
|
||||
[self writeCloseElement:NO];
|
||||
} else {
|
||||
if (emptyElement) {
|
||||
// go for linebreak + indentation + <END>
|
||||
[self writeLinebreak];
|
||||
[self writeIndentation];
|
||||
} else {
|
||||
// go for <START>characters<END>
|
||||
}
|
||||
}
|
||||
|
||||
// write standard end element
|
||||
[self write:@"</"];
|
||||
|
||||
if (namespaceURI) {
|
||||
NSString *prefix = [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
|
||||
if (!prefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Unknown namespace URI %@", namespaceURI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
if ([prefix length]) {
|
||||
[self write:prefix];
|
||||
[self write:@":"];
|
||||
}
|
||||
}
|
||||
|
||||
[self write:localName];
|
||||
[self write:@">"];
|
||||
|
||||
[self popNamespaceStack];
|
||||
[self popElementStack];
|
||||
|
||||
emptyElement = YES;
|
||||
openElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeEmptyElement:(NSString *)localName {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
|
||||
[self writeLinebreak];
|
||||
[self writeIndentation];
|
||||
|
||||
[self write:@"<"];
|
||||
[self write:localName];
|
||||
[self write:@" />"];
|
||||
|
||||
emptyElement = YES;
|
||||
openElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeEmptyElementWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
|
||||
[self writeLinebreak];
|
||||
[self writeIndentation];
|
||||
|
||||
[self write:@"<"];
|
||||
|
||||
if (namespaceURI) {
|
||||
NSString *prefix = [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
|
||||
if (!prefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Unknown namespace URI %@", namespaceURI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
if ([prefix length]) {
|
||||
[self write:prefix];
|
||||
[self write:@":"];
|
||||
}
|
||||
}
|
||||
|
||||
[self write:localName];
|
||||
[self write:@" />"];
|
||||
|
||||
emptyElement = YES;
|
||||
openElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeAttribute:(NSString *)localName value:(NSString *)value {
|
||||
[self writeAttributeWithNamespace:NULL localName:localName value:value];
|
||||
}
|
||||
|
||||
- (void)writeAttributeWithNamespace:(NSString *)namespaceURI localName:(NSString *)localName value:(NSString *)value {
|
||||
if (openElement) {
|
||||
[self write:@" "];
|
||||
|
||||
if (namespaceURI) {
|
||||
NSString *prefix = [namespaceURIPrefixMap objectForKey:namespaceURI];
|
||||
if (!prefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Unknown namespace URI %@", namespaceURI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
if ([prefix length]) {
|
||||
[self write:prefix];
|
||||
[self write:@":"];
|
||||
}
|
||||
}
|
||||
[self write:localName];
|
||||
[self write:@"=\""];
|
||||
[self writeEscape:value];
|
||||
[self write:@"\""];
|
||||
} else {
|
||||
// raise expection
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"No open start element" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDefaultNamespace:(NSString *)namespaceURI {
|
||||
[self setPrefix:EMPTY_STRING namespaceURI:namespaceURI];
|
||||
}
|
||||
|
||||
- (void)writeNamespace:(NSString *)prefix namespaceURI:(NSString *)namespaceURI {
|
||||
if (openElement) {
|
||||
[self setPrefix:prefix namespaceURI:namespaceURI];
|
||||
} else {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"No open start element" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeDefaultNamespace:(NSString *)namespaceURI {
|
||||
[self writeNamespace:EMPTY_STRING namespaceURI:namespaceURI];
|
||||
}
|
||||
|
||||
- (NSString *)getNamespaceURI:(NSString *)prefix {
|
||||
return [prefixNamespaceURIMap objectForKey:prefix];
|
||||
}
|
||||
|
||||
- (void)writeNamespaceToStream:(NSString *)prefix namespaceURI:(NSString *)namespaceURI {
|
||||
if (openElement) { // write the namespace now
|
||||
[self write:@" "];
|
||||
|
||||
NSString *xmlnsPrefix = [self getPrefix:XMLNS_NAMESPACE_URI];
|
||||
if (!xmlnsPrefix) {
|
||||
// raise exception
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Cannot declare namespace without namespace %@", XMLNS_NAMESPACE_URI]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
[self write:xmlnsPrefix]; // xmlns
|
||||
if ([prefix length]) {
|
||||
// write xmlns:prefix="namespaceURI" attribute
|
||||
|
||||
[self write:@":"]; // colon
|
||||
[self write:prefix]; // prefix
|
||||
} else {
|
||||
// write xmlns="namespaceURI" attribute
|
||||
}
|
||||
[self write:@"=\""];
|
||||
[self writeEscape:namespaceURI];
|
||||
[self write:@"\""];
|
||||
} else {
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException" reason:@"No open start element" userInfo:NULL]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeCharacters:(NSString *)text {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
|
||||
[self writeEscape:text];
|
||||
|
||||
emptyElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeComment:(NSString *)comment {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
[self write:@"<!--"];
|
||||
[self write:comment]; // no escape
|
||||
[self write:@"-->"];
|
||||
|
||||
emptyElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeProcessingInstruction:(NSString *)target data:(NSString *)data {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
[self write:@"<![CDATA["];
|
||||
[self write:target]; // no escape
|
||||
[self write:@" "];
|
||||
[self write:data]; // no escape
|
||||
[self write:@"]]>"];
|
||||
|
||||
emptyElement = NO;
|
||||
}
|
||||
|
||||
- (void)writeCData:(NSString *)cdata {
|
||||
if (openElement) {
|
||||
[self writeCloseElement:NO];
|
||||
}
|
||||
[self write:@"<![CDATA["];
|
||||
[self write:cdata]; // no escape
|
||||
[self write:@"]]>"];
|
||||
|
||||
emptyElement = NO;
|
||||
}
|
||||
|
||||
- (void)write:(NSString *)value {
|
||||
[writer appendString:value];
|
||||
}
|
||||
|
||||
- (void)writeEscape:(NSString *)value {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const UniChar *characters = CFStringGetCharactersPtr((CFStringRef)value);
|
||||
|
||||
if (characters) {
|
||||
// main flow
|
||||
[self writeEscapeCharacters:characters length:[value length]];
|
||||
} else {
|
||||
// we need to read/copy the characters for some reason, from the docs of CFStringGetCharactersPtr:
|
||||
// A pointer to a buffer of Unicode character or NULL if the internal storage of the CFString does not allow this to be returned efficiently.
|
||||
// Whether or not this function returns a valid pointer or NULL depends on many factors, all of which depend on how the string was created and
|
||||
// its properties. In addition, the function result might change between different releases and on different platforms. So do not count on
|
||||
// receiving a non- NULL result from this function under any circumstances (except when the object is created with
|
||||
// CFStringCreateMutableWithExternalCharactersNoCopy).
|
||||
|
||||
// we dont need the whole data length at once
|
||||
NSMutableData *data = [NSMutableData dataWithLength:256 * sizeof(UniChar)];
|
||||
|
||||
if (!data) {
|
||||
// raise exception - no more memory
|
||||
@throw([NSException exceptionWithName:@"XMLWriterException"
|
||||
reason:[NSString stringWithFormat:@"Could not allocate data buffer of %i unicode characters", 256]
|
||||
userInfo:NULL]);
|
||||
}
|
||||
|
||||
NSUInteger count = 0;
|
||||
do {
|
||||
NSUInteger length;
|
||||
if (count + 256 < [value length]) {
|
||||
length = 256;
|
||||
} else {
|
||||
length = [value length] - count;
|
||||
}
|
||||
|
||||
[value getCharacters:[data mutableBytes] range:NSMakeRange(count, length)];
|
||||
|
||||
[self writeEscapeCharacters:[data bytes] length:length];
|
||||
|
||||
count += length;
|
||||
} while (count < [value length]);
|
||||
|
||||
// buffers autorelease
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeEscapeCharacters:(const UniChar *)characters length:(NSUInteger)length {
|
||||
NSUInteger rangeStart = 0;
|
||||
CFIndex rangeLength = 0;
|
||||
|
||||
for (NSUInteger i = 0; i < length; i++) {
|
||||
UniChar c = characters[i];
|
||||
if (c <= 0xd7ff) {
|
||||
if (c >= 0x20) {
|
||||
switch (c) {
|
||||
case 34: {
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
[self write:@"""];
|
||||
|
||||
break;
|
||||
}
|
||||
// quot
|
||||
case 38: {
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
[self write:@"&"];
|
||||
|
||||
break;
|
||||
}
|
||||
// amp;
|
||||
case 60: {
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
|
||||
[self write:@"<"];
|
||||
|
||||
break;
|
||||
}
|
||||
// lt;
|
||||
case 62: {
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
|
||||
[self write:@">"];
|
||||
|
||||
break;
|
||||
}
|
||||
// gt;
|
||||
default: {
|
||||
// valid
|
||||
rangeLength++;
|
||||
|
||||
// note: we dont need to escape char 39 for ' because we use double quotes exclusively
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// set range start to next
|
||||
rangeLength = 0;
|
||||
rangeStart = i + 1;
|
||||
|
||||
} else {
|
||||
if (c == '\n' || c == '\r' || c == '\t') {
|
||||
// valid;
|
||||
rangeLength++;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
// invalid, skip
|
||||
}
|
||||
}
|
||||
} else if (c <= 0xFFFD) {
|
||||
// valid
|
||||
rangeLength++;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
// invalid, skip
|
||||
}
|
||||
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
|
||||
// set range start to next
|
||||
rangeLength = 0;
|
||||
rangeStart = i + 1;
|
||||
}
|
||||
|
||||
// write range if any
|
||||
if (rangeLength) {
|
||||
// main flow will probably write all characters here
|
||||
CFStringAppendCharacters((CFMutableStringRef)writer, characters + rangeStart, rangeLength);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeLinebreak {
|
||||
if (lineBreak) {
|
||||
[self write:lineBreak];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeIndentation {
|
||||
if (indentation) {
|
||||
for (int i = 0; i < level; i++) {
|
||||
[self write:indentation];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)flush {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
- (NSMutableString *)toString {
|
||||
return writer;
|
||||
}
|
||||
|
||||
- (NSData *)toData {
|
||||
if (encoding) {
|
||||
return [writer dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)encoding))
|
||||
allowLossyConversion:NO];
|
||||
} else {
|
||||
return [writer dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPrettyPrinting:(NSString *)aIndentation withLineBreak:(NSString *)aLineBreak {
|
||||
self.indentation = aIndentation;
|
||||
self.lineBreak = aLineBreak;
|
||||
}
|
||||
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudDomain.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudDomain.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudDomain.h
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudDomain : NSObject
|
||||
@property (nonatomic, strong, readonly) NSString *domain;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
- (instancetype)initWithDomain:(NSString *)domain;
|
||||
@end
|
||||
20
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudDomain.m
generated
Normal file
20
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudDomain.m
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// QCloudDomain.m
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudDomain.h"
|
||||
|
||||
@implementation QCloudDomain
|
||||
- (instancetype)initWithDomain:(NSString *)domain {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_domain = domain;
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHosts.h
generated
Normal file
16
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHosts.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// QCloudHosts.h
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudHosts : NSObject
|
||||
- (void)putDomain:(NSString *)domain ip:(NSString *)ip;
|
||||
- (NSArray *)queryIPForDomain:(NSString *)domain;
|
||||
- (BOOL)checkContainsIP:(NSString *)ip;
|
||||
- (void)clean;
|
||||
@end
|
||||
85
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHosts.m
generated
Normal file
85
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHosts.m
generated
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// QCloudHosts.m
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudHosts.h"
|
||||
#import "QCloudDomain.h"
|
||||
@implementation QCloudHosts {
|
||||
NSMutableDictionary *_cache;
|
||||
dispatch_queue_t _hostChangeQueue;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_hostChangeQueue = dispatch_queue_create("com.tencent.qcloud.host.resolve", DISPATCH_QUEUE_CONCURRENT);
|
||||
_cache = [NSMutableDictionary new];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)putDomain:(NSString *)domain ip:(NSString *)ip {
|
||||
#ifdef DEBUG
|
||||
NSParameterAssert(domain);
|
||||
NSParameterAssert(ip);
|
||||
#else
|
||||
if (!domain) {
|
||||
return;
|
||||
}
|
||||
if (!ip) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
dispatch_barrier_async(_hostChangeQueue, ^{
|
||||
NSMutableArray *array = [self->_cache objectForKey:domain];
|
||||
if (!array) {
|
||||
array = [NSMutableArray new];
|
||||
}
|
||||
if (![array containsObject:ip]) {
|
||||
[array addObject:ip];
|
||||
}
|
||||
self->_cache[domain] = array;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray *)queryIPForDomain:(NSString *)domain {
|
||||
__block NSArray *array = nil;
|
||||
dispatch_sync(_hostChangeQueue, ^(void) {
|
||||
array = [[self->_cache objectForKey:domain] copy];
|
||||
});
|
||||
return array;
|
||||
}
|
||||
|
||||
- (BOOL)checkContainsIP:(NSString *)ip {
|
||||
if (!ip) {
|
||||
return NO;
|
||||
}
|
||||
__block BOOL contained = NO;
|
||||
dispatch_sync(_hostChangeQueue, ^{
|
||||
for (NSArray *array in self->_cache.allValues) {
|
||||
for (NSString *cachedIP in array) {
|
||||
if ([cachedIP isEqualToString:ip]) {
|
||||
contained = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contained) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return contained;
|
||||
}
|
||||
|
||||
- (void)clean {
|
||||
dispatch_barrier_async(_hostChangeQueue, ^{
|
||||
[self->_cache removeAllObjects];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
78
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHttpDNS.h
generated
Normal file
78
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHttpDNS.h
generated
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// QCloudHttpDNS.h
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString *const kQCloudHttpDNSCacheReady;
|
||||
extern NSString *const kQCloudHttpDNSHost;
|
||||
|
||||
@class QCloudHosts;
|
||||
|
||||
@protocol QCloudHTTPDNSProtocol <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
解析domain,返回对应的ip地址。注意ip地址需要是有效的形式(xxx.xxx.xxx.xxx)否则会导致出错
|
||||
|
||||
@param domain 需要解析的域名
|
||||
@return ip地址
|
||||
*/
|
||||
- (NSString *)resolveDomain:(NSString *)domain;
|
||||
@end
|
||||
|
||||
@interface QCloudHttpDNS : NSObject
|
||||
@property (nonatomic, strong, readonly) QCloudHosts *hosts;
|
||||
|
||||
/**
|
||||
实现自定义解析ip的代理,当在记录里查询不到对应的host时,会向代理去再次请求解析。
|
||||
*/
|
||||
@property (nonatomic, weak) id<QCloudHTTPDNSProtocol> delegate;
|
||||
+ (instancetype)shareDNS;
|
||||
/**
|
||||
对于跟定的域名进行DNS缓存操作
|
||||
@param domain 需要缓存IP的域名
|
||||
@param error 如果过程出错,该字段表示错误信息
|
||||
@return 是否解析DNS成功
|
||||
*/
|
||||
- (BOOL)resolveDomain:(NSString *)domain error:(NSError **)error;
|
||||
|
||||
/**
|
||||
对于URLRequest进行IP重定向,如果改URLRequest原始指向的URL中的host对应的IP已经被解析了,则进行重定向操作,如果没有直接返回原始URLReqest
|
||||
@param request 需要被重定向的URLRequest
|
||||
@return 如果改URLRequest原始指向的URL中的host对应的IP已经被解析了,则进行重定向操作,如果没有直接返回原始URLReqest
|
||||
*/
|
||||
- (NSMutableURLRequest *)resolveURLRequestIfCan:(NSMutableURLRequest *)request;
|
||||
|
||||
/**
|
||||
判断一个IP是否是被解析出来,且被信任的
|
||||
@param ip 需要进行判断的IP
|
||||
@return 是否被信任
|
||||
*/
|
||||
- (BOOL)isTrustIP:(NSString *)ip;
|
||||
|
||||
/**
|
||||
手动添加一条hosts记录
|
||||
|
||||
@param ip ip地址
|
||||
@param domain 域名
|
||||
*/
|
||||
- (void)setIp:(NSString *)ip forDomain:(NSString *)domain;
|
||||
|
||||
- (NSString *)queryIPForHost:(NSString *)host;
|
||||
|
||||
- (NSArray *)queryIPsForHost:(NSString *)host;
|
||||
|
||||
/**
|
||||
Ping IP列表尝试拿到一个可用的IP
|
||||
*/
|
||||
- (NSString *)findHealthyIpFor:(NSString *)host;
|
||||
/**
|
||||
获取host对应的ip列表
|
||||
*/
|
||||
- (void)prepareFetchIPListForHost:(NSString *)host port:(NSString *)port;
|
||||
@end
|
||||
271
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHttpDNS.m
generated
Normal file
271
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudHttpDNS.m
generated
Normal file
@@ -0,0 +1,271 @@
|
||||
//
|
||||
// QCloudHttpDNS.m
|
||||
// TestHttps
|
||||
//
|
||||
// Created by tencent on 16/2/17.
|
||||
// Copyright © 2016年 dzpqzb. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudHttpDNS.h"
|
||||
#import "QCloudHosts.h"
|
||||
#import "QCloudLogger.h"
|
||||
#import "NSError+QCloudNetworking.h"
|
||||
#import "QCloudPingTester.h"
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#import "QCloudThreadSafeMutableDictionary.h"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#define IP_ADDR_IPv4 @"&&ipv4"
|
||||
#define IP_ADDR_IPv6 @"&&ipv6"
|
||||
NSString *const kQCloudHttpDNSCacheReady = @"kQCloudHttpDNSCacheReady";
|
||||
NSString *const kQCloudHttpDNSHost = @"host";
|
||||
|
||||
BOOL QCloudCheckIPVaild(NSString *ip) {
|
||||
return YES;
|
||||
}
|
||||
@interface QCloudHttpDNS () <WHPingDelegate>
|
||||
@property (nonatomic, strong) NSMutableArray<QCloudPingTester *> *pingTesters;
|
||||
@end
|
||||
|
||||
@implementation QCloudHttpDNS {
|
||||
QCloudHosts *_hosts;
|
||||
QCloudThreadSafeMutableDictionary *_ipHostMap;
|
||||
;
|
||||
}
|
||||
+ (instancetype)shareDNS {
|
||||
static QCloudHttpDNS *dns = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
dns = [QCloudHttpDNS new];
|
||||
});
|
||||
return dns;
|
||||
}
|
||||
|
||||
- (QCloudHosts *)hosts {
|
||||
return _hosts;
|
||||
}
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_hosts = [[QCloudHosts alloc] init];
|
||||
_ipHostMap = [[QCloudThreadSafeMutableDictionary alloc] init];
|
||||
_pingTesters = [NSMutableArray array];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)resolveDomain:(NSString *)domain error:(NSError **)error {
|
||||
NSString *ip;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(resolveDomain:)]) {
|
||||
ip = [self.delegate resolveDomain:domain];
|
||||
}
|
||||
if (!ip) {
|
||||
QCloudLogDebugE(@"HTTP",@"Cannot resolve domain %@", domain);
|
||||
if (NULL != error) {
|
||||
*error = [NSError qcloud_errorWithCode:kCFURLErrorDNSLookupFailed
|
||||
message:[NSString stringWithFormat:@"NetworkException:无法解析域名 %@", domain]];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (QCloudCheckIPVaild(ip)) {
|
||||
[_hosts putDomain:domain ip:[ip stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" "]]];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kQCloudHttpDNSCacheReady object:nil userInfo:@{ kQCloudHttpDNSHost : domain }];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)queryIPForHost:(NSString *)host {
|
||||
NSArray *hosts = [_hosts queryIPForDomain:host];
|
||||
// always use the last(lastest) one
|
||||
if (hosts.count) {
|
||||
return hosts.lastObject;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray *)queryIPsForHost:(NSString *)host {
|
||||
NSArray *ips = [_ipHostMap objectForKey:host];
|
||||
NSMutableArray * ipStrs = [NSMutableArray new];
|
||||
for (NSString *ip in ips) {
|
||||
[ipStrs addObject:[NSString stringWithFormat:@"%@/%@",host,ip]];
|
||||
}
|
||||
return ipStrs.copy;
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)resolveURLRequestIfCan:(NSMutableURLRequest *)request {
|
||||
if (!request) {
|
||||
return request;
|
||||
}
|
||||
NSString *host = request.URL.host;
|
||||
NSString *ip = [self queryIPForHost:host];
|
||||
// Give it second chance to reslove domain by itself
|
||||
if (!ip) {
|
||||
NSError *resolveError;
|
||||
[self resolveDomain:request.URL.host error:&resolveError];
|
||||
}
|
||||
ip = [self queryIPForHost:host];
|
||||
|
||||
if (!ip) {
|
||||
return request;
|
||||
}
|
||||
NSString *url = request.URL.absoluteString;
|
||||
NSRange range = [url rangeOfString:host];
|
||||
NSString *originHost = request.URL.host;
|
||||
if (range.location != NSNotFound && range.length > 0) {
|
||||
url = [url stringByReplacingOccurrencesOfString:host withString:ip options:0 range:range];
|
||||
NSMutableURLRequest *mReq = [request mutableCopy];
|
||||
mReq.URL = [NSURL URLWithString:url];
|
||||
[mReq setValue:originHost forHTTPHeaderField:@"Host"];
|
||||
return mReq;
|
||||
} else {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIp:(NSString *)ip forDomain:(NSString *)domain {
|
||||
if (QCloudCheckIPVaild(ip)) {
|
||||
[_hosts putDomain:domain ip:ip];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isTrustIP:(NSString *)ip {
|
||||
NSString *regex = @"\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b";
|
||||
NSPredicate *predictate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
|
||||
BOOL containsIP = [predictate evaluateWithObject:ip];
|
||||
if (!containsIP) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [_hosts checkContainsIP:ip];
|
||||
}
|
||||
|
||||
- (NSString *)findHealthyIpFor:(NSString *)host {
|
||||
NSArray *ipList = [_ipHostMap objectForKey:host];
|
||||
if (ipList.count) {
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
[self pingIp:ipList.lastObject host:host fulfil:sema];
|
||||
dispatch_wait(sema, DISPATCH_TIME_FOREVER);
|
||||
return [self queryIPForHost:host];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)pingIp:(NSString *)ip host:(NSString *)host fulfil:(dispatch_semaphore_t)sema {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString *ipAdd;
|
||||
if ([ip hasSuffix:IP_ADDR_IPv4]) {
|
||||
ipAdd = [ip stringByReplacingOccurrencesOfString:IP_ADDR_IPv4 withString:@""];
|
||||
} else if ([ip hasSuffix:IP_ADDR_IPv6]) {
|
||||
ipAdd = [ip stringByReplacingOccurrencesOfString:IP_ADDR_IPv6 withString:@""];
|
||||
}
|
||||
QCloudPingTester *pingTester = [[QCloudPingTester alloc] initWithIp:ipAdd host:host fulfil:sema];
|
||||
pingTester.delegate = self;
|
||||
[self->_pingTesters addObject:pingTester];
|
||||
[pingTester startPing];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pingTester:(QCloudPingTester *)pingTester didPingSucccessWithTime:(float)time withError:(NSError *)error {
|
||||
if (!error) {
|
||||
QCloudLogInfoPB(@"HTTP",@"ping的延迟是--->%f", time);
|
||||
[pingTester stopPing];
|
||||
[self setIp:pingTester.ip forDomain:pingTester.host];
|
||||
dispatch_semaphore_signal(pingTester.sema);
|
||||
} else {
|
||||
QCloudLogInfoPB(@"HTTP",@"网络不通过ip[%@]", pingTester.ip);
|
||||
[pingTester stopPing];
|
||||
NSMutableArray *ipList = [[_ipHostMap objectForKey:pingTester.host] mutableCopy];
|
||||
if (ipList.count) {
|
||||
[ipList removeLastObject];
|
||||
}
|
||||
[_ipHostMap setObject:ipList forKey:pingTester.host];
|
||||
if (ipList.count) {
|
||||
[self pingIp:ipList.lastObject host:pingTester.host fulfil:pingTester.sema];
|
||||
} else {
|
||||
dispatch_semaphore_signal(pingTester.sema);
|
||||
}
|
||||
}
|
||||
[_pingTesters removeObject:pingTester];
|
||||
}
|
||||
|
||||
- (void)prepareFetchIPListForHost:(NSString *)host port:(NSString *)port {
|
||||
NSArray *list = [_ipHostMap objectForKey:host];
|
||||
if (![_ipHostMap objectForKey:host] || !list.count) {
|
||||
list = getIPListFromToHost(host.UTF8String, port.UTF8String);
|
||||
if (list) {
|
||||
[_ipHostMap setObject:list forKey:host];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *getIPListFromToHost(const char *mHost, const char *mPort) {
|
||||
NSMutableArray *ipList = [NSMutableArray array];
|
||||
if (nil == mHost)
|
||||
return NULL;
|
||||
const char *newChar = "No";
|
||||
//返回的结构体信息链表
|
||||
struct addrinfo *res0;
|
||||
// 配置需要返回的结构体信息组成
|
||||
struct addrinfo hints;
|
||||
// 返回的地址信息
|
||||
struct addrinfo *res;
|
||||
int n;
|
||||
|
||||
// 置空结构体
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
|
||||
hints.ai_flags = AI_DEFAULT;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((n = getaddrinfo(mHost, "http", &hints, &res0)) != 0) {
|
||||
QCloudLogInfoPB(@"HTTP",@"getaddrinfo error: %s", gai_strerror(n));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 *addr6;
|
||||
struct sockaddr_in *addr;
|
||||
NSString *NewStr = NULL;
|
||||
char ipbuf[32];
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
if (res->ai_family == AF_INET6) {
|
||||
addr6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||
newChar = inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, sizeof(ipbuf));
|
||||
if(newChar == NULL){
|
||||
continue;
|
||||
}
|
||||
NSString *TempA = [[NSString alloc] initWithCString:(const char *)newChar encoding:NSASCIIStringEncoding];
|
||||
NSString *TempB = [NSString stringWithUTF8String:IP_ADDR_IPv6.UTF8String];
|
||||
|
||||
NewStr = [TempA stringByAppendingString:TempB];
|
||||
|
||||
} else {
|
||||
addr = (struct sockaddr_in *)res->ai_addr;
|
||||
newChar = inet_ntop(AF_INET, &addr->sin_addr, ipbuf, sizeof(ipbuf));
|
||||
if(newChar == NULL){
|
||||
continue;
|
||||
}
|
||||
NSString *TempA = [[NSString alloc] initWithCString:(const char *)newChar encoding:NSASCIIStringEncoding];
|
||||
NSString *TempB = [NSString stringWithUTF8String:IP_ADDR_IPv4.UTF8String];
|
||||
|
||||
NewStr = [TempA stringByAppendingString:TempB];
|
||||
}
|
||||
|
||||
[ipList addObject:NewStr];
|
||||
QCloudLogInfoPB(@"HTTP",@"host[%s] ipList:%@", mHost, ipList);
|
||||
}
|
||||
|
||||
if(res0!=NULL){
|
||||
freeaddrinfo(res0);
|
||||
}
|
||||
|
||||
return ipList;
|
||||
}
|
||||
@end
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
40
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudPingTester.h
generated
Normal file
40
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudPingTester.h
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
//
|
||||
// WHPingTester.h
|
||||
// BigVPN
|
||||
//
|
||||
// Created by wanghe on 2017/5/11.
|
||||
// Copyright © 2017年 wanghe. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QCloudSimplePing.h"
|
||||
@class QCloudPingTester;
|
||||
@protocol WHPingDelegate <NSObject>
|
||||
@optional
|
||||
- (void)pingTester:(QCloudPingTester *)pingTest didPingSucccessWithTime:(float)time withError:(NSError *)error;
|
||||
@end
|
||||
|
||||
@interface QCloudPingTester : NSObject <SimplePingDelegate>
|
||||
@property (nonatomic, weak, readwrite) id<WHPingDelegate> delegate;
|
||||
@property (nonatomic, readonly) NSString *ip;
|
||||
@property (nonatomic, readonly) NSString *host;
|
||||
@property (nonatomic) dispatch_semaphore_t sema;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithIp:(NSString *)ip host:(NSString *)host fulfil:(dispatch_semaphore_t)sema NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)startPing;
|
||||
- (void)stopPing;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WHPingStatus) {
|
||||
WHPingStatusSending = 0 << 0,
|
||||
WHPingStatusTimeout = 1 << 1,
|
||||
WHPingStatusSended = 2 << 2,
|
||||
};
|
||||
|
||||
@interface QCloudPingItem : NSObject
|
||||
//@property(nonatomic, assign) WHPingStatus status;
|
||||
@property (nonatomic, assign) uint16_t sequence;
|
||||
|
||||
@end
|
||||
108
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudPingTester.m
generated
Normal file
108
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudPingTester.m
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
|
||||
//
|
||||
// WHPingTester.m
|
||||
// BigVPN
|
||||
//
|
||||
// Created by wanghe on 2017/5/11.
|
||||
// Copyright © 2017年 wanghe. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QCloudPingTester.h"
|
||||
#import "QCloudCore.h"
|
||||
@interface QCloudPingTester () <SimplePingDelegate> {
|
||||
NSTimer *_timer;
|
||||
NSDate *_beginDate;
|
||||
}
|
||||
@property (nonatomic, strong) QCloudSimplePing *simplePing;
|
||||
@property (nonatomic) NSString *ip;
|
||||
@property (nonatomic) NSString *host;
|
||||
@property (nonatomic, strong) NSMutableArray<QCloudPingItem *> *pingItems;
|
||||
@end
|
||||
|
||||
@implementation QCloudPingTester
|
||||
|
||||
- (instancetype)initWithIp:(NSString *)ip host:(NSString *)host fulfil:(dispatch_semaphore_t)sema {
|
||||
if (self = [super init]) {
|
||||
self.ip = ip;
|
||||
self.host = host;
|
||||
self.sema = sema;
|
||||
self.simplePing = [[QCloudSimplePing alloc] initWithHostName:ip];
|
||||
self.simplePing.delegate = self;
|
||||
self.simplePing.addressStyle = SimplePingAddressStyleAny;
|
||||
|
||||
self.pingItems = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startPing {
|
||||
[self.simplePing start];
|
||||
}
|
||||
|
||||
- (void)stopPing {
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
[self.simplePing stop];
|
||||
}
|
||||
|
||||
- (void)actionTimer {
|
||||
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendPingData) userInfo:nil repeats:YES];
|
||||
}
|
||||
|
||||
- (void)sendPingData {
|
||||
[self.simplePing sendPingWithData:nil];
|
||||
}
|
||||
|
||||
#pragma mark Ping Delegate
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didStartWithAddress:(NSData *)address {
|
||||
[self actionTimer];
|
||||
}
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didFailWithError:(NSError *)error {
|
||||
QCloudLogError(@"[ERROR]ping失败,error: %@", error);
|
||||
}
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber {
|
||||
QCloudPingItem *item = [QCloudPingItem new];
|
||||
item.sequence = sequenceNumber;
|
||||
[self.pingItems addObject:item];
|
||||
|
||||
_beginDate = [NSDate date];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if ([self.pingItems containsObject:item]) {
|
||||
QCloudLogError(@"[ERROR]超时---->");
|
||||
[self.pingItems removeObject:item];
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pingTester:didPingSucccessWithTime:withError:)]) {
|
||||
[self.delegate pingTester:self didPingSucccessWithTime:0 withError:[NSError errorWithDomain:NSURLErrorDomain code:111 userInfo:nil]];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error {
|
||||
QCloudLogError(@"[ERROR]发包失败:%@", error);
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pingTester:didPingSucccessWithTime:withError:)]) {
|
||||
[self.delegate pingTester:self didPingSucccessWithTime:0 withError:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber {
|
||||
float delayTime = [[NSDate date] timeIntervalSinceDate:_beginDate] * 1000;
|
||||
[self.pingItems enumerateObjectsUsingBlock:^(QCloudPingItem *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
|
||||
if (obj.sequence == sequenceNumber) {
|
||||
[self.pingItems removeObject:obj];
|
||||
}
|
||||
}];
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pingTester:didPingSucccessWithTime:withError:)]) {
|
||||
[self.delegate pingTester:self didPingSucccessWithTime:delayTime withError:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudPingItem
|
||||
|
||||
@end
|
||||
275
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudSimplePing.h
generated
Normal file
275
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudSimplePing.h
generated
Normal file
@@ -0,0 +1,275 @@
|
||||
|
||||
|
||||
/*
|
||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
||||
See LICENSE.txt for this sample’s licensing information
|
||||
|
||||
Abstract:
|
||||
An object wrapper around the low-level BSD Sockets ping function.
|
||||
*/
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#include <AssertMacros.h> // for __Check_Compile_Time
|
||||
#include <sys/socket.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SimplePingDelegate;
|
||||
|
||||
/*! Controls the IP address version used by SimplePing instances.
|
||||
*/
|
||||
|
||||
typedef NS_ENUM(NSInteger, SimplePingAddressStyle) {
|
||||
SimplePingAddressStyleAny, ///< Use the first IPv4 or IPv6 address found; the default.
|
||||
SimplePingAddressStyleICMPv4, ///< Use the first IPv4 address found.
|
||||
SimplePingAddressStyleICMPv6 ///< Use the first IPv6 address found.
|
||||
};
|
||||
|
||||
/*! An object wrapper around the low-level BSD Sockets ping function.
|
||||
* \details To use the class create an instance, set the delegate and call `-start`
|
||||
* to start the instance on the current run loop. If things go well you'll soon get the
|
||||
* `-simplePing:didStartWithAddress:` delegate callback. From there you can can call
|
||||
* `-sendPingWithData:` to send a ping and you'll receive the
|
||||
* `-simplePing:didReceivePingResponsePacket:sequenceNumber:` and
|
||||
* `-simplePing:didReceiveUnexpectedPacket:` delegate callbacks as ICMP packets arrive.
|
||||
*
|
||||
* The class can be used from any thread but the use of any single instance must be
|
||||
* confined to a specific thread and that thread must run its run loop.
|
||||
*/
|
||||
|
||||
@interface QCloudSimplePing : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! Initialise the object to ping the specified host.
|
||||
* \param hostName The DNS name of the host to ping; an IPv4 or IPv6 address in string form will
|
||||
* work here.
|
||||
* \returns The initialised object.
|
||||
*/
|
||||
|
||||
- (instancetype)initWithHostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! A copy of the value passed to `-initWithHostName:`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *hostName;
|
||||
|
||||
/*! The delegate for this object.
|
||||
* \details Delegate callbacks are schedule in the default run loop mode of the run loop of the
|
||||
* thread that calls `-start`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, weak, readwrite, nullable) id<SimplePingDelegate> delegate;
|
||||
|
||||
/*! Controls the IP address version used by the object.
|
||||
* \details You should set this value before starting the object.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readwrite) SimplePingAddressStyle addressStyle;
|
||||
|
||||
/*! The address being pinged.
|
||||
* \details The contents of the NSData is a (struct sockaddr) of some form. The
|
||||
* value is nil while the object is stopped and remains nil on start until
|
||||
* `-simplePing:didStartWithAddress:` is called.
|
||||
*/
|
||||
|
||||
@property (nonatomic, copy, readonly, nullable) NSData *hostAddress;
|
||||
|
||||
/*! The address family for `hostAddress`, or `AF_UNSPEC` if that's nil.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readonly) sa_family_t hostAddressFamily;
|
||||
|
||||
/*! The identifier used by pings by this object.
|
||||
* \details When you create an instance of this object it generates a random identifier
|
||||
* that it uses to identify its own pings.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readonly) uint16_t identifier;
|
||||
|
||||
/*! The next sequence number to be used by this object.
|
||||
* \details This value starts at zero and increments each time you send a ping (safely
|
||||
* wrapping back to zero if necessary). The sequence number is included in the ping,
|
||||
* allowing you to match up requests and responses, and thus calculate ping times and
|
||||
* so on.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readonly) uint16_t nextSequenceNumber;
|
||||
|
||||
/*! Starts the object.
|
||||
* \details You should set up the delegate and any ping parameters before calling this.
|
||||
*
|
||||
* If things go well you'll soon get the `-simplePing:didStartWithAddress:` delegate
|
||||
* callback, at which point you can start sending pings (via `-sendPingWithData:`) and
|
||||
* will start receiving ICMP packets (either ping responses, via the
|
||||
* `-simplePing:didReceivePingResponsePacket:sequenceNumber:` delegate callback, or
|
||||
* unsolicited ICMP packets, via the `-simplePing:didReceiveUnexpectedPacket:` delegate
|
||||
* callback).
|
||||
*
|
||||
* If the object fails to start, typically because `hostName` doesn't resolve, you'll get
|
||||
* the `-simplePing:didFailWithError:` delegate callback.
|
||||
*
|
||||
* It is not correct to start an already started object.
|
||||
*/
|
||||
|
||||
- (void)start;
|
||||
|
||||
/*! Sends a ping packet containing the specified data.
|
||||
* \details Sends an actual ping.
|
||||
*
|
||||
* The object must be started when you call this method and, on starting the object, you must
|
||||
* wait for the `-simplePing:didStartWithAddress:` delegate callback before calling it.
|
||||
* \param data Some data to include in the ping packet, after the ICMP header, or nil if you
|
||||
* want the packet to include a standard 56 byte payload (resulting in a standard 64 byte
|
||||
* ping).
|
||||
*/
|
||||
|
||||
- (void)sendPingWithData:(nullable NSData *)data;
|
||||
|
||||
/*! Stops the object.
|
||||
* \details You should call this when you're done pinging.
|
||||
*
|
||||
* It's safe to call this on an object that's stopped.
|
||||
*/
|
||||
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
/*! A delegate protocol for the SimplePing class.
|
||||
*/
|
||||
|
||||
@protocol SimplePingDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/*! A SimplePing delegate callback, called once the object has started up.
|
||||
* \details This is called shortly after you start the object to tell you that the
|
||||
* object has successfully started. On receiving this callback, you can call
|
||||
* `-sendPingWithData:` to send pings.
|
||||
*
|
||||
* If the object didn't start, `-simplePing:didFailWithError:` is called instead.
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param address The address that's being pinged; at the time this delegate callback
|
||||
* is made, this will have the same value as the `hostAddress` property.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didStartWithAddress:(NSData *)address;
|
||||
|
||||
/*! A SimplePing delegate callback, called if the object fails to start up.
|
||||
* \details This is called shortly after you start the object to tell you that the
|
||||
* object has failed to start. The most likely cause of failure is a problem
|
||||
* resolving `hostName`.
|
||||
*
|
||||
* By the time this callback is called, the object has stopped (that is, you don't
|
||||
* need to call `-stop` yourself).
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param error Describes the failure.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didFailWithError:(NSError *)error;
|
||||
|
||||
/*! A SimplePing delegate callback, called when the object has successfully sent a ping packet.
|
||||
* \details Each call to `-sendPingWithData:` will result in either a
|
||||
* `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a
|
||||
* `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you
|
||||
* stop the object before you get the callback). These callbacks are currently delivered
|
||||
* synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not
|
||||
* considered API.
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param packet The packet that was sent; this includes the ICMP header (`ICMPHeader`) and the
|
||||
* data you passed to `-sendPingWithData:` but does not include any IP-level headers.
|
||||
* \param sequenceNumber The ICMP sequence number of that packet.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber;
|
||||
|
||||
/*! A SimplePing delegate callback, called when the object fails to send a ping packet.
|
||||
* \details Each call to `-sendPingWithData:` will result in either a
|
||||
* `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a
|
||||
* `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you
|
||||
* stop the object before you get the callback). These callbacks are currently delivered
|
||||
* synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not
|
||||
* considered API.
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param packet The packet that was not sent; see `-simplePing:didSendPacket:sequenceNumber:`
|
||||
* for details.
|
||||
* \param sequenceNumber The ICMP sequence number of that packet.
|
||||
* \param error Describes the failure.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error;
|
||||
|
||||
/*! A SimplePing delegate callback, called when the object receives a ping response.
|
||||
* \details If the object receives an ping response that matches a ping request that it
|
||||
* sent, it informs the delegate via this callback. Matching is primarily done based on
|
||||
* the ICMP identifier, although other criteria are used as well.
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that
|
||||
* follows that in the ICMP message but does not include any IP-level headers.
|
||||
* \param sequenceNumber The ICMP sequence number of that packet.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber;
|
||||
|
||||
/*! A SimplePing delegate callback, called when the object receives an unmatched ICMP message.
|
||||
* \details If the object receives an ICMP message that does not match a ping request that it
|
||||
* sent, it informs the delegate via this callback. The nature of ICMP handling in a
|
||||
* BSD kernel makes this a common event because, when an ICMP message arrives, it is
|
||||
* delivered to all ICMP sockets.
|
||||
*
|
||||
* IMPORTANT: This callback is especially common when using IPv6 because IPv6 uses ICMP
|
||||
* for important network management functions. For example, IPv6 routers periodically
|
||||
* send out Router Advertisement (RA) packets via Neighbor Discovery Protocol (NDP), which
|
||||
* is implemented on top of ICMP.
|
||||
*
|
||||
* For more on matching, see the discussion associated with
|
||||
* `-simplePing:didReceivePingResponsePacket:sequenceNumber:`.
|
||||
* \param pinger The object issuing the callback.
|
||||
* \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that
|
||||
* follows that in the ICMP message but does not include any IP-level headers.
|
||||
*/
|
||||
|
||||
- (void)simplePing:(QCloudSimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark * ICMP On-The-Wire Format
|
||||
|
||||
/*! Describes the on-the-wire header format for an ICMP ping.
|
||||
* \details This defines the header structure of ping packets on the wire. Both IPv4 and
|
||||
* IPv6 use the same basic structure.
|
||||
*
|
||||
* This is declared in the header because clients of SimplePing might want to use
|
||||
* it parse received ping packets.
|
||||
*/
|
||||
|
||||
struct ICMPHeader {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t checksum;
|
||||
uint16_t identifier;
|
||||
uint16_t sequenceNumber;
|
||||
// data...
|
||||
};
|
||||
typedef struct ICMPHeader ICMPHeader;
|
||||
|
||||
__Check_Compile_Time(sizeof(ICMPHeader) == 8);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, type) == 0);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, code) == 1);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6);
|
||||
|
||||
enum {
|
||||
ICMPv4TypeEchoRequest = 8, ///< The ICMP `type` for a ping request; in this case `code` is always 0.
|
||||
ICMPv4TypeEchoReply = 0 ///< The ICMP `type` for a ping response; in this case `code` is always 0.
|
||||
};
|
||||
|
||||
enum {
|
||||
ICMPv6TypeEchoRequest = 128, ///< The ICMP `type` for a ping request; in this case `code` is always 0.
|
||||
ICMPv6TypeEchoReply = 129 ///< The ICMP `type` for a ping response; in this case `code` is always 0.
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
788
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudSimplePing.m
generated
Normal file
788
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DNSCache/QCloudSimplePing.m
generated
Normal file
@@ -0,0 +1,788 @@
|
||||
|
||||
|
||||
#import "QCloudSimplePing.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
|
||||
#pragma mark * IPv4 and ICMPv4 On-The-Wire Format
|
||||
|
||||
/*! Describes the on-the-wire header format for an IPv4 packet.
|
||||
* \details This defines the header structure of IPv4 packets on the wire. We need
|
||||
* this in order to skip this header in the IPv4 case, where the kernel passes
|
||||
* it to us for no obvious reason.
|
||||
*/
|
||||
|
||||
struct IPv4Header {
|
||||
uint8_t versionAndHeaderLength;
|
||||
uint8_t differentiatedServices;
|
||||
uint16_t totalLength;
|
||||
uint16_t identification;
|
||||
uint16_t flagsAndFragmentOffset;
|
||||
uint8_t timeToLive;
|
||||
uint8_t protocol;
|
||||
uint16_t headerChecksum;
|
||||
uint8_t sourceAddress[4];
|
||||
uint8_t destinationAddress[4];
|
||||
// options...
|
||||
// data...
|
||||
};
|
||||
typedef struct IPv4Header IPv4Header;
|
||||
|
||||
__Check_Compile_Time(sizeof(IPv4Header) == 20);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, versionAndHeaderLength) == 0);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, differentiatedServices) == 1);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, totalLength) == 2);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, identification) == 4);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, flagsAndFragmentOffset) == 6);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, timeToLive) == 8);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, protocol) == 9);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, headerChecksum) == 10);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, sourceAddress) == 12);
|
||||
__Check_Compile_Time(offsetof(IPv4Header, destinationAddress) == 16);
|
||||
|
||||
/*! Calculates an IP checksum.
|
||||
* \details This is the standard BSD checksum code, modified to use modern types.
|
||||
* \param buffer A pointer to the data to checksum.
|
||||
* \param bufferLen The length of that data.
|
||||
* \returns The checksum value, in network byte order.
|
||||
*/
|
||||
|
||||
static uint16_t in_cksum(const void *buffer, size_t bufferLen) {
|
||||
//
|
||||
size_t bytesLeft;
|
||||
int32_t sum;
|
||||
const uint16_t *cursor;
|
||||
union {
|
||||
uint16_t us;
|
||||
uint8_t uc[2];
|
||||
} last;
|
||||
uint16_t answer;
|
||||
|
||||
bytesLeft = bufferLen;
|
||||
sum = 0;
|
||||
cursor = buffer;
|
||||
|
||||
/*
|
||||
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
|
||||
* sequential 16 bit words to it, and at the end, fold back all the
|
||||
* carry bits from the top 16 bits into the lower 16 bits.
|
||||
*/
|
||||
while (bytesLeft > 1) {
|
||||
sum += *cursor;
|
||||
cursor += 1;
|
||||
bytesLeft -= 2;
|
||||
}
|
||||
|
||||
/* mop up an odd byte, if necessary */
|
||||
if (bytesLeft == 1) {
|
||||
last.uc[0] = *(const uint8_t *)cursor;
|
||||
last.uc[1] = 0;
|
||||
sum += last.us;
|
||||
}
|
||||
|
||||
/* add back carry outs from top 16 bits to low 16 bits */
|
||||
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
||||
sum += (sum >> 16); /* add carry */
|
||||
answer = (uint16_t)~sum; /* truncate to 16 bits */
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
#pragma mark * SimplePing
|
||||
|
||||
@interface QCloudSimplePing ()
|
||||
|
||||
// read/write versions of public properties
|
||||
|
||||
@property (nonatomic, copy, readwrite, nullable) NSData *hostAddress;
|
||||
@property (nonatomic, assign, readwrite) uint16_t nextSequenceNumber;
|
||||
|
||||
// private properties
|
||||
|
||||
/*! True if nextSequenceNumber has wrapped from 65535 to 0.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readwrite) BOOL nextSequenceNumberHasWrapped;
|
||||
|
||||
/*! A host object for name-to-address resolution.
|
||||
*/
|
||||
|
||||
@property (nonatomic, strong, readwrite, nullable) CFHostRef host __attribute__((NSObject));
|
||||
|
||||
/*! A socket object for ICMP send and receive.
|
||||
*/
|
||||
|
||||
@property (nonatomic, strong, readwrite, nullable) CFSocketRef socket __attribute__((NSObject));
|
||||
|
||||
@end
|
||||
|
||||
@implementation QCloudSimplePing
|
||||
|
||||
- (instancetype)initWithHostName:(NSString *)hostName {
|
||||
NSParameterAssert(hostName != nil);
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_hostName = [hostName copy];
|
||||
self->_identifier = (uint16_t)arc4random();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self stop];
|
||||
// Double check that -stop took care of _host and _socket.
|
||||
assert(self->_host == NULL);
|
||||
assert(self->_socket == NULL);
|
||||
}
|
||||
|
||||
- (sa_family_t)hostAddressFamily {
|
||||
sa_family_t result;
|
||||
|
||||
result = AF_UNSPEC;
|
||||
if ((self.hostAddress != nil) && (self.hostAddress.length >= sizeof(struct sockaddr))) {
|
||||
result = ((const struct sockaddr *)self.hostAddress.bytes)->sa_family;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Shuts down the pinger object and tell the delegate about the error.
|
||||
* \param error Describes the failure.
|
||||
*/
|
||||
|
||||
- (void)didFailWithError:(NSError *)error {
|
||||
id<SimplePingDelegate> strongDelegate;
|
||||
|
||||
assert(error != nil);
|
||||
|
||||
// We retain ourselves temporarily because it's common for the delegate method
|
||||
// to release its last reference to us, which causes -dealloc to be called here.
|
||||
// If we then reference self on the return path, things go badly. I don't think
|
||||
// that happens currently, but I've got into the habit of doing this as a
|
||||
// defensive measure.
|
||||
|
||||
CFAutorelease(CFBridgingRetain(self));
|
||||
|
||||
[self stop];
|
||||
strongDelegate = self.delegate;
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didFailWithError:)]) {
|
||||
[strongDelegate simplePing:self didFailWithError:error];
|
||||
}
|
||||
}
|
||||
|
||||
/*! Shuts down the pinger object and tell the delegate about the error.
|
||||
* \details This converts the CFStreamError to an NSError and then call through to
|
||||
* -didFailWithError: to do the real work.
|
||||
* \param streamError Describes the failure.
|
||||
*/
|
||||
|
||||
- (void)didFailWithHostStreamError:(CFStreamError)streamError {
|
||||
NSDictionary *userInfo;
|
||||
NSError *error;
|
||||
|
||||
if (streamError.domain == kCFStreamErrorDomainNetDB) {
|
||||
userInfo = @{ (id)kCFGetAddrInfoFailureKey : @(streamError.error) };
|
||||
} else {
|
||||
userInfo = nil;
|
||||
}
|
||||
error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo];
|
||||
|
||||
[self didFailWithError:error];
|
||||
}
|
||||
|
||||
/*! Builds a ping packet from the supplied parameters.
|
||||
* \param type The packet type, which is different for IPv4 and IPv6.
|
||||
* \param payload Data to place after the ICMP header.
|
||||
* \param requiresChecksum Determines whether a checksum is calculated (IPv4) or not (IPv6).
|
||||
* \returns A ping packet suitable to be passed to the kernel.
|
||||
*/
|
||||
|
||||
- (NSData *)pingPacketWithType:(uint8_t)type payload:(NSData *)payload requiresChecksum:(BOOL)requiresChecksum {
|
||||
NSMutableData *packet;
|
||||
ICMPHeader *icmpPtr;
|
||||
|
||||
packet = [NSMutableData dataWithLength:sizeof(*icmpPtr) + payload.length];
|
||||
if (packet == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
icmpPtr = packet.mutableBytes;
|
||||
icmpPtr->type = type;
|
||||
icmpPtr->code = 0;
|
||||
icmpPtr->checksum = 0;
|
||||
icmpPtr->identifier = OSSwapHostToBigInt16(self.identifier);
|
||||
icmpPtr->sequenceNumber = OSSwapHostToBigInt16(self.nextSequenceNumber);
|
||||
memcpy(&icmpPtr[1], [payload bytes], [payload length]);
|
||||
|
||||
if (requiresChecksum) {
|
||||
// The IP checksum routine returns a 16-bit number that's already in correct byte order
|
||||
// (due to wacky 1's complement maths), so we just put it into the packet as a 16-bit unit.
|
||||
|
||||
icmpPtr->checksum = in_cksum(packet.bytes, packet.length);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
- (void)sendPingWithData:(NSData *)data {
|
||||
int err;
|
||||
NSData *payload;
|
||||
NSData *packet;
|
||||
ssize_t bytesSent;
|
||||
id<SimplePingDelegate> strongDelegate;
|
||||
|
||||
// data may be nil
|
||||
// NSParameterAssert(self.hostAddress != nil); // gotta wait for -simplePing:didStartWithAddress:
|
||||
|
||||
if (self.hostAddress == nil) {
|
||||
return;
|
||||
}
|
||||
// Construct the ping packet.
|
||||
|
||||
payload = data;
|
||||
if (payload == nil) {
|
||||
payload = [[NSString stringWithFormat:@"%28zd bottles of beer on the wall", (ssize_t)99 - (size_t)(self.nextSequenceNumber % 100)]
|
||||
dataUsingEncoding:NSASCIIStringEncoding];
|
||||
assert(payload != nil);
|
||||
|
||||
// Our dummy payload is sized so that the resulting ICMP packet, including the ICMPHeader, is
|
||||
// 64-bytes, which makes it easier to recognise our packets on the wire.
|
||||
|
||||
assert([payload length] == 56);
|
||||
}
|
||||
|
||||
switch (self.hostAddressFamily) {
|
||||
case AF_INET: {
|
||||
packet = [self pingPacketWithType:ICMPv4TypeEchoRequest payload:payload requiresChecksum:YES];
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
packet = [self pingPacketWithType:ICMPv6TypeEchoRequest payload:payload requiresChecksum:NO];
|
||||
} break;
|
||||
default: {
|
||||
return;
|
||||
} break;
|
||||
}
|
||||
// assert(packet != nil);
|
||||
if (packet == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the packet.
|
||||
|
||||
if (self.socket == NULL) {
|
||||
bytesSent = -1;
|
||||
err = EBADF;
|
||||
} else {
|
||||
bytesSent
|
||||
= sendto(CFSocketGetNative(self.socket), packet.bytes, packet.length, 0, self.hostAddress.bytes, (socklen_t)self.hostAddress.length);
|
||||
err = 0;
|
||||
if (bytesSent < 0) {
|
||||
err = errno;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the results of the send.
|
||||
|
||||
strongDelegate = self.delegate;
|
||||
if ((bytesSent > 0) && (((NSUInteger)bytesSent) == packet.length)) {
|
||||
// Complete success. Tell the client.
|
||||
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didSendPacket:sequenceNumber:)]) {
|
||||
[strongDelegate simplePing:self didSendPacket:packet sequenceNumber:self.nextSequenceNumber];
|
||||
}
|
||||
} else {
|
||||
NSError *error;
|
||||
|
||||
// Some sort of failure. Tell the client.
|
||||
|
||||
if (err == 0) {
|
||||
err = ENOBUFS; // This is not a hugely descriptor error, alas.
|
||||
}
|
||||
error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil];
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didFailToSendPacket:sequenceNumber:error:)]) {
|
||||
[strongDelegate simplePing:self didFailToSendPacket:packet sequenceNumber:self.nextSequenceNumber error:error];
|
||||
}
|
||||
}
|
||||
|
||||
self.nextSequenceNumber += 1;
|
||||
if (self.nextSequenceNumber == 0) {
|
||||
self.nextSequenceNumberHasWrapped = YES;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Calculates the offset of the ICMP header within an IPv4 packet.
|
||||
* \details In the IPv4 case the kernel returns us a buffer that includes the
|
||||
* IPv4 header. We're not interested in that, so we have to skip over it.
|
||||
* This code does a rough check of the IPv4 header and, if it looks OK,
|
||||
* returns the offset of the ICMP header.
|
||||
* \param packet The IPv4 packet, as returned to us by the kernel.
|
||||
* \returns The offset of the ICMP header, or NSNotFound.
|
||||
*/
|
||||
|
||||
+ (NSUInteger)icmpHeaderOffsetInIPv4Packet:(NSData *)packet {
|
||||
// Returns the offset of the ICMPv4Header within an IP packet.
|
||||
NSUInteger result;
|
||||
const struct IPv4Header *ipPtr;
|
||||
size_t ipHeaderLength;
|
||||
|
||||
result = NSNotFound;
|
||||
if (packet.length >= (sizeof(IPv4Header) + sizeof(ICMPHeader))) {
|
||||
ipPtr = (const IPv4Header *)packet.bytes;
|
||||
if (((ipPtr->versionAndHeaderLength & 0xF0) == 0x40) && // IPv4
|
||||
(ipPtr->protocol == IPPROTO_ICMP)) {
|
||||
ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t);
|
||||
if (packet.length >= (ipHeaderLength + sizeof(ICMPHeader))) {
|
||||
result = ipHeaderLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Checks whether the specified sequence number is one we sent.
|
||||
* \param sequenceNumber The incoming sequence number.
|
||||
* \returns YES if the sequence number looks like one we sent.
|
||||
*/
|
||||
|
||||
- (BOOL)validateSequenceNumber:(uint16_t)sequenceNumber {
|
||||
if (self.nextSequenceNumberHasWrapped) {
|
||||
// If the sequence numbers have wrapped that we can't reliably check
|
||||
// whether this is a sequence number we sent. Rather, we check to see
|
||||
// whether the sequence number is within the last 120 sequence numbers
|
||||
// we sent. Note that the uint16_t subtraction here does the right
|
||||
// thing regardless of the wrapping.
|
||||
//
|
||||
// Why 120? Well, if we send one ping per second, 120 is 2 minutes, which
|
||||
// is the standard "max time a packet can bounce around the Internet" value.
|
||||
return ((uint16_t)(self.nextSequenceNumber - sequenceNumber)) < (uint16_t)120;
|
||||
} else {
|
||||
return sequenceNumber < self.nextSequenceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Checks whether an incoming IPv4 packet looks like a ping response.
|
||||
* \details This routine modifies this `packet` data! It does this for two reasons:
|
||||
*
|
||||
* * It needs to zero out the `checksum` field of the ICMPHeader in order to do
|
||||
* its checksum calculation.
|
||||
*
|
||||
* * It removes the IPv4 header from the front of the packet.
|
||||
* \param packet The IPv4 packet, as returned to us by the kernel.
|
||||
* \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number.
|
||||
* \returns YES if the packet looks like a reasonable IPv4 ping response.
|
||||
*/
|
||||
|
||||
- (BOOL)validatePing4ResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr {
|
||||
BOOL result;
|
||||
NSUInteger icmpHeaderOffset;
|
||||
ICMPHeader *icmpPtr;
|
||||
uint16_t receivedChecksum;
|
||||
uint16_t calculatedChecksum;
|
||||
|
||||
result = NO;
|
||||
|
||||
icmpHeaderOffset = [[self class] icmpHeaderOffsetInIPv4Packet:packet];
|
||||
if (icmpHeaderOffset != NSNotFound) {
|
||||
icmpPtr = (struct ICMPHeader *)(((uint8_t *)packet.mutableBytes) + icmpHeaderOffset);
|
||||
|
||||
receivedChecksum = icmpPtr->checksum;
|
||||
icmpPtr->checksum = 0;
|
||||
calculatedChecksum = in_cksum(icmpPtr, packet.length - icmpHeaderOffset);
|
||||
icmpPtr->checksum = receivedChecksum;
|
||||
|
||||
if (receivedChecksum == calculatedChecksum) {
|
||||
if ((icmpPtr->type == ICMPv4TypeEchoReply) && (icmpPtr->code == 0)) {
|
||||
if (OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier) {
|
||||
uint16_t sequenceNumber;
|
||||
|
||||
sequenceNumber = OSSwapBigToHostInt16(icmpPtr->sequenceNumber);
|
||||
if ([self validateSequenceNumber:sequenceNumber]) {
|
||||
// Remove the IPv4 header off the front of the data we received, leaving us with
|
||||
// just the ICMP header and the ping payload.
|
||||
[packet replaceBytesInRange:NSMakeRange(0, icmpHeaderOffset) withBytes:NULL length:0];
|
||||
|
||||
*sequenceNumberPtr = sequenceNumber;
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Checks whether an incoming IPv6 packet looks like a ping response.
|
||||
* \param packet The IPv6 packet, as returned to us by the kernel; note that this routine
|
||||
* could modify this data but does not need to in the IPv6 case.
|
||||
* \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number.
|
||||
* \returns YES if the packet looks like a reasonable IPv4 ping response.
|
||||
*/
|
||||
|
||||
- (BOOL)validatePing6ResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr {
|
||||
BOOL result;
|
||||
const ICMPHeader *icmpPtr;
|
||||
|
||||
result = NO;
|
||||
|
||||
if (packet.length >= sizeof(*icmpPtr)) {
|
||||
icmpPtr = packet.bytes;
|
||||
|
||||
// In the IPv6 case we don't check the checksum because that's hard (we need to
|
||||
// cook up an IPv6 pseudo header and we don't have the ingredients) and unnecessary
|
||||
// (the kernel has already done this check).
|
||||
|
||||
if ((icmpPtr->type == ICMPv6TypeEchoReply) && (icmpPtr->code == 0)) {
|
||||
if (OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier) {
|
||||
uint16_t sequenceNumber;
|
||||
|
||||
sequenceNumber = OSSwapBigToHostInt16(icmpPtr->sequenceNumber);
|
||||
if ([self validateSequenceNumber:sequenceNumber]) {
|
||||
*sequenceNumberPtr = sequenceNumber;
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Checks whether an incoming packet looks like a ping response.
|
||||
* \param packet The packet, as returned to us by the kernel; note that may end up modifying
|
||||
* this data.
|
||||
* \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number.
|
||||
* \returns YES if the packet looks like a reasonable IPv4 ping response.
|
||||
*/
|
||||
|
||||
- (BOOL)validatePingResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr {
|
||||
BOOL result;
|
||||
|
||||
switch (self.hostAddressFamily) {
|
||||
case AF_INET: {
|
||||
result = [self validatePing4ResponsePacket:packet sequenceNumber:sequenceNumberPtr];
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
result = [self validatePing6ResponsePacket:packet sequenceNumber:sequenceNumberPtr];
|
||||
} break;
|
||||
default: {
|
||||
// assert(NO);
|
||||
result = NO;
|
||||
} break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Reads data from the ICMP socket.
|
||||
* \details Called by the socket handling code (SocketReadCallback) to process an ICMP
|
||||
* message waiting on the socket.
|
||||
*/
|
||||
|
||||
- (void)readData {
|
||||
int err;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrLen;
|
||||
ssize_t bytesRead;
|
||||
void *buffer;
|
||||
enum { kBufferSize = 65535 };
|
||||
|
||||
// 65535 is the maximum IP packet size, which seems like a reasonable bound
|
||||
// here (plus it's what <x-man-page://8/ping> uses).
|
||||
|
||||
buffer = malloc(kBufferSize);
|
||||
// assert(buffer != NULL);
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually read the data. We use recvfrom(), and thus get back the source address,
|
||||
// but we don't actually do anything with it. It would be trivial to pass it to
|
||||
// the delegate but we don't need it in this example.
|
||||
|
||||
addrLen = sizeof(addr);
|
||||
bytesRead = recvfrom(CFSocketGetNative(self.socket), buffer, kBufferSize, 0, (struct sockaddr *)&addr, &addrLen);
|
||||
err = 0;
|
||||
if (bytesRead < 0) {
|
||||
err = errno;
|
||||
}
|
||||
|
||||
// Process the data we read.
|
||||
|
||||
if (bytesRead > 0) {
|
||||
NSMutableData *packet;
|
||||
id<SimplePingDelegate> strongDelegate;
|
||||
uint16_t sequenceNumber;
|
||||
|
||||
packet = [NSMutableData dataWithBytes:buffer length:(NSUInteger)bytesRead];
|
||||
// assert(packet != nil);
|
||||
if (packet == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We got some data, pass it up to our client.
|
||||
|
||||
strongDelegate = self.delegate;
|
||||
if ([self validatePingResponsePacket:packet sequenceNumber:&sequenceNumber]) {
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didReceivePingResponsePacket:sequenceNumber:)]) {
|
||||
[strongDelegate simplePing:self didReceivePingResponsePacket:packet sequenceNumber:sequenceNumber];
|
||||
}
|
||||
} else {
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didReceiveUnexpectedPacket:)]) {
|
||||
[strongDelegate simplePing:self didReceiveUnexpectedPacket:packet];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We failed to read the data, so shut everything down.
|
||||
|
||||
if (err == 0) {
|
||||
err = EPIPE;
|
||||
}
|
||||
[self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
// Note that we don't loop back trying to read more data. Rather, we just
|
||||
// let CFSocket call us again.
|
||||
}
|
||||
|
||||
/*! The callback for our CFSocket object.
|
||||
* \details This simply routes the call to our `-readData` method.
|
||||
* \param s See the documentation for CFSocketCallBack.
|
||||
* \param type See the documentation for CFSocketCallBack.
|
||||
* \param address See the documentation for CFSocketCallBack.
|
||||
* \param data See the documentation for CFSocketCallBack.
|
||||
* \param info See the documentation for CFSocketCallBack; this is actually a pointer to the
|
||||
* 'owning' object.
|
||||
*/
|
||||
|
||||
static void SocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
|
||||
// This C routine is called by CFSocket when there's data waiting on our
|
||||
// ICMP socket. It just redirects the call to Objective-C code.
|
||||
QCloudSimplePing *obj;
|
||||
|
||||
obj = (__bridge QCloudSimplePing *)info;
|
||||
assert([obj isKindOfClass:[QCloudSimplePing class]]);
|
||||
|
||||
#pragma unused(s)
|
||||
assert(s == obj.socket);
|
||||
#pragma unused(type)
|
||||
assert(type == kCFSocketReadCallBack);
|
||||
#pragma unused(address)
|
||||
assert(address == nil);
|
||||
#pragma unused(data)
|
||||
assert(data == nil);
|
||||
|
||||
[obj readData];
|
||||
}
|
||||
|
||||
/*! Starts the send and receive infrastructure.
|
||||
* \details This is called once we've successfully resolved `hostName` in to
|
||||
* `hostAddress`. It's responsible for setting up the socket for sending and
|
||||
* receiving pings.
|
||||
*/
|
||||
|
||||
- (void)startWithHostAddress {
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
// assert(self.hostAddress != nil);
|
||||
if (self.hostAddress == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the socket.
|
||||
|
||||
fd = -1;
|
||||
err = 0;
|
||||
switch (self.hostAddressFamily) {
|
||||
case AF_INET: {
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (fd < 0) {
|
||||
err = errno;
|
||||
}
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
|
||||
if (fd < 0) {
|
||||
err = errno;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
err = EPROTONOSUPPORT;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (err != 0) {
|
||||
[self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
|
||||
} else {
|
||||
CFSocketContext context = { 0, (__bridge void *)(self), NULL, NULL, NULL };
|
||||
CFRunLoopSourceRef rls;
|
||||
id<SimplePingDelegate> strongDelegate;
|
||||
|
||||
// Wrap it in a CFSocket and schedule it on the runloop.
|
||||
|
||||
self.socket = (CFSocketRef)CFAutorelease(CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, SocketReadCallback, &context));
|
||||
assert(self.socket != NULL);
|
||||
|
||||
// The socket will now take care of cleaning up our file descriptor.
|
||||
|
||||
assert(CFSocketGetSocketFlags(self.socket) & kCFSocketCloseOnInvalidate);
|
||||
fd = -1;
|
||||
|
||||
rls = CFSocketCreateRunLoopSource(NULL, self.socket, 0);
|
||||
assert(rls != NULL);
|
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
|
||||
|
||||
CFRelease(rls);
|
||||
|
||||
strongDelegate = self.delegate;
|
||||
if ((strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didStartWithAddress:)]) {
|
||||
[strongDelegate simplePing:self didStartWithAddress:self.hostAddress];
|
||||
}
|
||||
}
|
||||
assert(fd == -1);
|
||||
}
|
||||
|
||||
/*! Processes the results of our name-to-address resolution.
|
||||
* \details Called by our CFHost resolution callback (HostResolveCallback) when host
|
||||
* resolution is complete. We just latch the first appropriate address and kick
|
||||
* off the send and receive infrastructure.
|
||||
*/
|
||||
|
||||
- (void)hostResolutionDone {
|
||||
Boolean resolved;
|
||||
NSArray *addresses;
|
||||
|
||||
// Find the first appropriate address.
|
||||
|
||||
addresses = (__bridge NSArray *)CFHostGetAddressing(self.host, &resolved);
|
||||
if (resolved && (addresses != nil)) {
|
||||
resolved = false;
|
||||
for (NSData *address in addresses) {
|
||||
const struct sockaddr *addrPtr;
|
||||
|
||||
addrPtr = (const struct sockaddr *)address.bytes;
|
||||
if (address.length >= sizeof(struct sockaddr)) {
|
||||
switch (addrPtr->sa_family) {
|
||||
case AF_INET: {
|
||||
if (self.addressStyle != SimplePingAddressStyleICMPv6) {
|
||||
self.hostAddress = address;
|
||||
resolved = true;
|
||||
}
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
if (self.addressStyle != SimplePingAddressStyleICMPv4) {
|
||||
self.hostAddress = address;
|
||||
resolved = true;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (resolved) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're done resolving, so shut that down.
|
||||
|
||||
[self stopHostResolution];
|
||||
|
||||
// If all is OK, start the send and receive infrastructure, otherwise stop.
|
||||
|
||||
if (resolved) {
|
||||
[self startWithHostAddress];
|
||||
} else {
|
||||
[self didFailWithError:[NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorHostNotFound userInfo:nil]];
|
||||
}
|
||||
}
|
||||
|
||||
/*! The callback for our CFHost object.
|
||||
* \details This simply routes the call to our `-hostResolutionDone` or
|
||||
* `-didFailWithHostStreamError:` methods.
|
||||
* \param theHost See the documentation for CFHostClientCallBack.
|
||||
* \param typeInfo See the documentation for CFHostClientCallBack.
|
||||
* \param error See the documentation for CFHostClientCallBack.
|
||||
* \param info See the documentation for CFHostClientCallBack; this is actually a pointer to
|
||||
* the 'owning' object.
|
||||
*/
|
||||
|
||||
static void HostResolveCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info) {
|
||||
// This C routine is called by CFHost when the host resolution is complete.
|
||||
// It just redirects the call to the appropriate Objective-C method.
|
||||
QCloudSimplePing *obj;
|
||||
|
||||
obj = (__bridge QCloudSimplePing *)info;
|
||||
assert([obj isKindOfClass:[QCloudSimplePing class]]);
|
||||
|
||||
#pragma unused(theHost)
|
||||
assert(theHost == obj.host);
|
||||
#pragma unused(typeInfo)
|
||||
assert(typeInfo == kCFHostAddresses);
|
||||
|
||||
if ((error != NULL) && (error->domain != 0)) {
|
||||
[obj didFailWithHostStreamError:*error];
|
||||
} else {
|
||||
[obj hostResolutionDone];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
Boolean success;
|
||||
CFHostClientContext context = { 0, (__bridge void *)(self), NULL, NULL, NULL };
|
||||
CFStreamError streamError;
|
||||
|
||||
assert(self.host == NULL);
|
||||
assert(self.hostAddress == nil);
|
||||
|
||||
self.host = (CFHostRef)CFAutorelease(CFHostCreateWithName(NULL, (__bridge CFStringRef)self.hostName));
|
||||
// assert(self.host != NULL);
|
||||
if (self.host == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFHostSetClient(self.host, HostResolveCallback, &context);
|
||||
|
||||
CFHostScheduleWithRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
|
||||
success = CFHostStartInfoResolution(self.host, kCFHostAddresses, &streamError);
|
||||
if (!success) {
|
||||
[self didFailWithHostStreamError:streamError];
|
||||
}
|
||||
}
|
||||
|
||||
/*! Stops the name-to-address resolution infrastructure.
|
||||
*/
|
||||
|
||||
- (void)stopHostResolution {
|
||||
// Shut down the CFHost.
|
||||
if (self.host != NULL) {
|
||||
CFHostSetClient(self.host, NULL, NULL);
|
||||
CFHostUnscheduleFromRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
self.host = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Stops the send and receive infrastructure.
|
||||
*/
|
||||
|
||||
- (void)stopSocket {
|
||||
if (self.socket != NULL) {
|
||||
CFSocketInvalidate(self.socket);
|
||||
self.socket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
[self stopHostResolution];
|
||||
[self stopSocket];
|
||||
|
||||
// Junk the host address on stop. If the client calls -start again, we'll
|
||||
// re-resolve the host name.
|
||||
|
||||
self.hostAddress = NULL;
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetBody.h
generated
Normal file
17
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetBody.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// QCloudFileOffsetBody.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/5/23.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudFileOffsetBody : NSObject
|
||||
@property (nonatomic, strong, readonly) NSURL *fileURL;
|
||||
@property (nonatomic, assign, readonly) NSUInteger offset;
|
||||
@property (nonatomic, assign, readonly) NSUInteger sliceLength;
|
||||
@property (nonatomic, assign) NSUInteger index;
|
||||
- (instancetype)initWithFile:(NSURL *)fileURL offset:(NSUInteger)offset slice:(NSUInteger)slice;
|
||||
@end
|
||||
23
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetBody.m
generated
Normal file
23
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetBody.m
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// QCloudFileOffsetBody.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/5/23.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudFileOffsetBody.h"
|
||||
|
||||
@implementation QCloudFileOffsetBody
|
||||
- (instancetype)initWithFile:(NSURL *)fileURL offset:(NSUInteger)offset slice:(NSUInteger)slice {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
_fileURL = fileURL;
|
||||
_offset = offset;
|
||||
_sliceLength = slice;
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
19
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetStream.h
generated
Normal file
19
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetStream.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// QCloudFileOffsetStream.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/5/22.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QCloudFileOffsetStream : NSInputStream
|
||||
@property (nonatomic, assign) NSUInteger offset;
|
||||
@property (nonatomic, assign) NSUInteger sliceLength;
|
||||
- (instancetype)initWithFileAtPath:(NSString *)path NS_UNAVAILABLE;
|
||||
- (instancetype)initWithData:(NSData *)data NS_UNAVAILABLE;
|
||||
- (instancetype)initWithURL:(NSURL *)url NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithFileAtPath:(NSString *)path offset:(NSUInteger)offset slice:(NSUInteger)sliceLength;
|
||||
@end
|
||||
127
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetStream.m
generated
Normal file
127
Pods/QCloudCore/QCloudCore/Classes/Base/QCLOUDRestNet/DataForm/QCloudFileOffsetStream.m
generated
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// QCloudFileOffsetStream.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Dong Zhao on 2017/5/22.
|
||||
//
|
||||
//
|
||||
|
||||
#import "QCloudFileOffsetStream.h"
|
||||
#import "QCloudFileUtils.h"
|
||||
@interface NSStream ()
|
||||
@property (readwrite) NSStreamStatus streamStatus;
|
||||
@property (readwrite, copy) NSError *streamError;
|
||||
@end
|
||||
|
||||
@interface NSInputStream ()
|
||||
- (void)open;
|
||||
- (void)close;
|
||||
@end
|
||||
@interface QCloudFileOffsetStream ()
|
||||
@property (nonatomic, assign) NSUInteger contentLength;
|
||||
@property (nonatomic, assign) NSUInteger readLength;
|
||||
@property (nonatomic, assign) NSStreamStatus status;
|
||||
@property (nonatomic, strong) NSFileHandle *fileReadHandler;
|
||||
@property (nonatomic, strong) NSString *path;
|
||||
@end
|
||||
|
||||
@implementation QCloudFileOffsetStream
|
||||
@synthesize streamError;
|
||||
@synthesize streamStatus;
|
||||
|
||||
- (instancetype)initWithFileAtPath:(NSString *)path offset:(NSUInteger)offset slice:(NSUInteger)sliceLength {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
_offset = offset;
|
||||
_sliceLength = sliceLength;
|
||||
_contentLength = QCloudFileSize(path);
|
||||
_path = path;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
_fileReadHandler = [NSFileHandle fileHandleForReadingAtPath:self.path];
|
||||
[_fileReadHandler seekToFileOffset:_offset];
|
||||
self.status = NSStreamStatusOpen;
|
||||
_readLength = 0;
|
||||
}
|
||||
|
||||
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
|
||||
if (self.status == NSStreamStatusClosed) {
|
||||
return 0;
|
||||
}
|
||||
NSUInteger ureadLength = _sliceLength - _readLength;
|
||||
NSUInteger willReadLength = MIN(ureadLength, len);
|
||||
if (willReadLength <= 0) {
|
||||
return 0;
|
||||
}
|
||||
NSData *data = [_fileReadHandler readDataOfLength:willReadLength];
|
||||
memcpy(buffer, [data bytes], willReadLength);
|
||||
_readLength += willReadLength;
|
||||
return willReadLength;
|
||||
}
|
||||
|
||||
- (NSError *)streamError {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSStreamStatus)streamStatus {
|
||||
if (![self hasBytesAvailable]) {
|
||||
self.status = NSStreamStatusAtEnd;
|
||||
}
|
||||
return self.status;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[_fileReadHandler closeFile];
|
||||
_fileReadHandler = nil;
|
||||
self.status = NSStreamStatusClosed;
|
||||
_readLength = 0;
|
||||
}
|
||||
- (BOOL)getBuffer:(uint8_t *_Nullable *_Nonnull)buffer length:(NSUInteger *)len {
|
||||
return NO;
|
||||
}
|
||||
- (BOOL)hasBytesAvailable {
|
||||
if (_offset + _readLength >= _contentLength) {
|
||||
return NO;
|
||||
}
|
||||
if (_readLength >= _sliceLength) {
|
||||
return NO;
|
||||
}
|
||||
if (_offset + _readLength < _sliceLength) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)propertyForKey:(__unused NSString *)key {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)setProperty:(__unused id)property forKey:(__unused NSString *)key {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop forMode:(__unused NSString *)mode {
|
||||
}
|
||||
|
||||
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop forMode:(__unused NSString *)mode {
|
||||
}
|
||||
|
||||
#pragma mark - Undocumented CFReadStream Bridged Methods
|
||||
|
||||
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop forMode:(__unused CFStringRef)aMode {
|
||||
}
|
||||
|
||||
- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop forMode:(__unused CFStringRef)aMode {
|
||||
}
|
||||
|
||||
- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
|
||||
callback:(__unused CFReadStreamClientCallBack)inCallback
|
||||
context:(__unused CFStreamClientContext *)inContext {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user