解决了语音上传到问题,接下来要解决下载播放问题
This commit is contained in:
22
Pods/Qiniu/LICENSE
generated
Executable file
22
Pods/Qiniu/LICENSE
generated
Executable file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2016 Qiniu, Ltd.<sdk@qiniu.com>
|
||||
|
||||
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.
|
||||
|
||||
55
Pods/Qiniu/QiniuSDK/BigData/QNPipeline.h
generated
Normal file
55
Pods/Qiniu/QiniuSDK/BigData/QNPipeline.h
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// QNPipeline.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by BaiLong on 2017/7/25.
|
||||
// Copyright © 2017年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef QNPipeline_h
|
||||
#define QNPipeline_h
|
||||
|
||||
@class QNResponseInfo;
|
||||
|
||||
@interface QNPipelineConfig : NSObject
|
||||
|
||||
/**
|
||||
* 上报打点域名
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *host;
|
||||
|
||||
/**
|
||||
* 超时时间 单位 秒
|
||||
*/
|
||||
@property (assign) UInt32 timeoutInterval;
|
||||
|
||||
- (instancetype)initWithHost:(NSString *)host;
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* 上传完成后的回调函数
|
||||
*
|
||||
* @param info 上下文信息,包括状态码,错误值
|
||||
*/
|
||||
typedef void (^QNPipelineCompletionHandler)(QNResponseInfo *info);
|
||||
|
||||
@interface QNPipeline : NSObject
|
||||
|
||||
- (instancetype)init:(QNPipelineConfig *)config;
|
||||
|
||||
- (void)pumpRepo:(NSString *)repo
|
||||
event:(NSDictionary *)data
|
||||
token:(NSString *)token
|
||||
handler:(QNPipelineCompletionHandler)handler;
|
||||
|
||||
- (void)pumpRepo:(NSString *)repo
|
||||
events:(NSArray<NSDictionary *> *)data
|
||||
token:(NSString *)token
|
||||
handler:(QNPipelineCompletionHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* QNPipeline_h */
|
||||
154
Pods/Qiniu/QiniuSDK/BigData/QNPipeline.m
generated
Normal file
154
Pods/Qiniu/QiniuSDK/BigData/QNPipeline.m
generated
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// QNPipeline.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by BaiLong on 2017/7/25.
|
||||
// Copyright © 2017年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "QNSessionManager.h"
|
||||
|
||||
#import "QNPipeline.h"
|
||||
|
||||
@implementation QNPipelineConfig
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithHost:@"https://pipeline.qiniu.com"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithHost:(NSString*)host {
|
||||
if (self = [super init]) {
|
||||
_host = host;
|
||||
_timeoutInterval = 10;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface QNPipeline ()
|
||||
|
||||
@property (nonatomic) QNSessionManager *httpManager;
|
||||
@property (nonatomic) QNPipelineConfig* config;
|
||||
|
||||
+ (NSDateFormatter*)dateFormatter;
|
||||
|
||||
@end
|
||||
|
||||
static NSString* buildString(NSObject* obj) {
|
||||
NSString* v;
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber* num = (NSNumber*)obj;
|
||||
if (num == (void*)kCFBooleanFalse) {
|
||||
v = @"false";
|
||||
} else if (num == (void*)kCFBooleanTrue) {
|
||||
v = @"true";
|
||||
} else if (!strcmp(num.objCType, @encode(BOOL))) {
|
||||
if ([num intValue] == 0) {
|
||||
v = @"false";
|
||||
} else {
|
||||
v = @"true";
|
||||
}
|
||||
} else {
|
||||
v = num.stringValue;
|
||||
}
|
||||
} else if ([obj isKindOfClass:[NSString class]]) {
|
||||
v = (NSString*)obj;
|
||||
v = [v stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
|
||||
v = [v stringByReplacingOccurrencesOfString:@"\t" withString:@"\\t"];
|
||||
} else if ([obj isKindOfClass:[NSDictionary class]] || [obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSSet class]]) {
|
||||
v = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:obj options:kNilOptions error:nil] encoding:NSUTF8StringEncoding];
|
||||
} else if ([obj isKindOfClass:[NSDate class]]) {
|
||||
v = [[QNPipeline dateFormatter] stringFromDate:(NSDate*)obj];
|
||||
} else {
|
||||
v = [obj description];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static void formatPoint(NSDictionary* event, NSMutableString* buffer) {
|
||||
[event enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSObject* obj, BOOL* stop) {
|
||||
if (obj == nil || [obj isEqual:[NSNull null]]) {
|
||||
return;
|
||||
}
|
||||
[buffer appendString:key];
|
||||
[buffer appendString:@"="];
|
||||
[buffer appendString:buildString(obj)];
|
||||
[buffer appendString:@"\t"];
|
||||
}];
|
||||
NSRange range = NSMakeRange(buffer.length - 1, 1);
|
||||
[buffer replaceCharactersInRange:range withString:@"\n"];
|
||||
}
|
||||
|
||||
static NSMutableString* formatPoints(NSArray<NSDictionary*>* events) {
|
||||
NSMutableString* str = [NSMutableString new];
|
||||
[events enumerateObjectsUsingBlock:^(NSDictionary* _Nonnull obj, NSUInteger idx, BOOL* _Nonnull stop) {
|
||||
formatPoint(obj, str);
|
||||
}];
|
||||
return str;
|
||||
}
|
||||
|
||||
@implementation QNPipeline
|
||||
|
||||
- (instancetype)init:(QNPipelineConfig*)config {
|
||||
if (self = [super init]) {
|
||||
if (config == nil) {
|
||||
config = [QNPipelineConfig new];
|
||||
}
|
||||
_config = config;
|
||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
|
||||
_httpManager = [[QNSessionManager alloc] initWithProxy:nil timeout:config.timeoutInterval urlConverter:nil];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)pumpRepo:(NSString*)repo
|
||||
event:(NSDictionary*)data
|
||||
token:(NSString*)token
|
||||
handler:(QNPipelineCompletionHandler)handler {
|
||||
NSMutableString* str = [NSMutableString new];
|
||||
formatPoint(data, str);
|
||||
[self pumpRepo:repo string:str token:token handler:handler];
|
||||
}
|
||||
|
||||
- (void)pumpRepo:(NSString*)repo
|
||||
events:(NSArray<NSDictionary*>*)data
|
||||
token:(NSString*)token
|
||||
handler:(QNPipelineCompletionHandler)handler {
|
||||
NSMutableString* str = formatPoints(data);
|
||||
[self pumpRepo:repo string:str token:token handler:handler];
|
||||
}
|
||||
|
||||
- (NSString*)url:(NSString*)repo {
|
||||
return [NSString stringWithFormat:@"%@/v2/repos/%@/data", _config.host, repo];
|
||||
}
|
||||
|
||||
- (void)pumpRepo:(NSString*)repo
|
||||
string:(NSString*)str
|
||||
token:(NSString*)token
|
||||
handler:(QNPipelineCompletionHandler)handler {
|
||||
NSDictionary* headers = @{ @"Authorization" : token,
|
||||
@"Content-Type" : @"text/plain" };
|
||||
[_httpManager post:[self url:repo] withData:[str dataUsingEncoding:NSUTF8StringEncoding] withParams:nil withHeaders:headers withIdentifier:nil withCompleteBlock:^(QNResponseInfo *httpResponseInfo, NSDictionary *respBody) {
|
||||
handler(httpResponseInfo);
|
||||
} withProgressBlock:nil withCancelBlock:nil withAccess:nil];
|
||||
}
|
||||
|
||||
+ (NSDateFormatter*)dateFormatter {
|
||||
static NSDateFormatter* formatter = nil;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [NSDateFormatter new];
|
||||
[formatter setLocale:[NSLocale currentLocale]];
|
||||
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"];
|
||||
[formatter setTimeZone:[NSTimeZone defaultTimeZone]];
|
||||
});
|
||||
|
||||
return formatter;
|
||||
}
|
||||
|
||||
@end
|
||||
50
Pods/Qiniu/QiniuSDK/BigData/QNSessionManager.h
generated
Normal file
50
Pods/Qiniu/QiniuSDK/BigData/QNSessionManager.h
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNConfiguration.h"
|
||||
|
||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
|
||||
|
||||
@class QNResponseInfo;
|
||||
|
||||
typedef void (^QNInternalProgressBlock)(long long totalBytesWritten, long long totalBytesExpectedToWrite);
|
||||
typedef void (^QNCompleteBlock)(QNResponseInfo *httpResponseInfo, NSDictionary *respBody);
|
||||
typedef BOOL (^QNCancelBlock)(void);
|
||||
|
||||
|
||||
@interface QNSessionManager : NSObject
|
||||
|
||||
- (instancetype)initWithProxy:(NSDictionary *)proxyDict
|
||||
timeout:(UInt32)timeout
|
||||
urlConverter:(QNUrlConvert)converter;
|
||||
|
||||
- (void)multipartPost:(NSString *)url
|
||||
withData:(NSData *)data
|
||||
withParams:(NSDictionary *)params
|
||||
withFileName:(NSString *)key
|
||||
withMimeType:(NSString *)mime
|
||||
withIdentifier:(NSString *)identifier
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock
|
||||
withProgressBlock:(QNInternalProgressBlock)progressBlock
|
||||
withCancelBlock:(QNCancelBlock)cancelBlock
|
||||
withAccess:(NSString *)access;
|
||||
|
||||
- (void)post:(NSString *)url
|
||||
withData:(NSData *)data
|
||||
withParams:(NSDictionary *)params
|
||||
withHeaders:(NSDictionary *)headers
|
||||
withIdentifier:(NSString *)identifier
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock
|
||||
withProgressBlock:(QNInternalProgressBlock)progressBlock
|
||||
withCancelBlock:(QNCancelBlock)cancelBlock
|
||||
withAccess:(NSString *)access;
|
||||
|
||||
- (void)get:(NSString *)url
|
||||
withHeaders:(NSDictionary *)headers
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock;
|
||||
|
||||
- (void)invalidateSessionWithIdentifier:(NSString *)identifier;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
265
Pods/Qiniu/QiniuSDK/BigData/QNSessionManager.m
generated
Normal file
265
Pods/Qiniu/QiniuSDK/BigData/QNSessionManager.m
generated
Normal file
@@ -0,0 +1,265 @@
|
||||
//
|
||||
// QNHttpManager.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/1.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNSessionManager.h"
|
||||
#import "QNUserAgent.h"
|
||||
|
||||
#import "QNResponseInfo.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
|
||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
|
||||
|
||||
typedef void (^QNSessionComplete)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error);
|
||||
@interface QNSessionDelegateHandler : NSObject <NSURLSessionDataDelegate>
|
||||
|
||||
@property (nonatomic, copy) QNInternalProgressBlock progressBlock;
|
||||
@property (nonatomic, copy) QNCancelBlock cancelBlock;
|
||||
@property (nonatomic, copy) QNSessionComplete completeBlock;
|
||||
@property (nonatomic, strong) NSData *responseData;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNSessionDelegateHandler
|
||||
|
||||
#pragma mark - NSURLSessionDataDelegate
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
|
||||
_responseData = data;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(nullable NSError *)error {
|
||||
// bytes_sent & bytes_total
|
||||
self.completeBlock(_responseData, task.response, error);
|
||||
[session finishTasksAndInvalidate];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
||||
didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
||||
|
||||
if (_progressBlock) {
|
||||
_progressBlock(totalBytesSent, totalBytesExpectedToSend);
|
||||
}
|
||||
if (_cancelBlock && _cancelBlock()) {
|
||||
[task cancel];
|
||||
}
|
||||
}
|
||||
|
||||
- (uint64_t)getTimeintervalWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate {
|
||||
|
||||
if (!startDate || !endDate) return 0;
|
||||
NSTimeInterval interval = [endDate timeIntervalSinceDate:startDate];
|
||||
return interval * 1000;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface QNSessionManager ()
|
||||
@property UInt32 timeout;
|
||||
@property (nonatomic, strong) QNUrlConvert converter;
|
||||
@property (nonatomic, strong) NSDictionary *proxyDict;
|
||||
@property (nonatomic, strong) NSOperationQueue *delegateQueue;
|
||||
@property (nonatomic, strong) NSMutableArray *sessionArray;
|
||||
@property (nonatomic, strong) NSLock *lock;
|
||||
@end
|
||||
|
||||
@implementation QNSessionManager
|
||||
|
||||
- (instancetype)initWithProxy:(NSDictionary *)proxyDict
|
||||
timeout:(UInt32)timeout
|
||||
urlConverter:(QNUrlConvert)converter {
|
||||
if (self = [super init]) {
|
||||
_delegateQueue = [[NSOperationQueue alloc] init];
|
||||
_timeout = timeout;
|
||||
_converter = converter;
|
||||
_proxyDict = proxyDict;
|
||||
_sessionArray = [NSMutableArray array];
|
||||
|
||||
_lock = [[NSLock alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithProxy:nil timeout:60 urlConverter:nil];
|
||||
}
|
||||
|
||||
- (void)sendRequest:(NSMutableURLRequest *)request
|
||||
withIdentifier:(NSString *)identifier
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock
|
||||
withProgressBlock:(QNInternalProgressBlock)progressBlock
|
||||
withCancelBlock:(QNCancelBlock)cancelBlock
|
||||
withAccess:(NSString *)access {
|
||||
|
||||
NSString *domain = request.URL.host;
|
||||
NSString *u = request.URL.absoluteString;
|
||||
NSURL *url = request.URL;
|
||||
if (_converter != nil) {
|
||||
url = [[NSURL alloc] initWithString:_converter(u)];
|
||||
request.URL = url;
|
||||
domain = url.host;
|
||||
}
|
||||
|
||||
request.qn_domain = request.URL.host;
|
||||
[request setTimeoutInterval:_timeout];
|
||||
[request setValue:[[QNUserAgent sharedInstance] getUserAgent:access] forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:nil forHTTPHeaderField:@"Accept-Language"];
|
||||
|
||||
QNSessionDelegateHandler *delegate = [[QNSessionDelegateHandler alloc] init];
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
configuration.connectionProxyDictionary = _proxyDict ? _proxyDict : nil;
|
||||
|
||||
__block NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:_delegateQueue];
|
||||
[_sessionArray addObject:@{@"identifier":identifier,@"session":session}];
|
||||
|
||||
delegate.cancelBlock = cancelBlock;
|
||||
delegate.progressBlock = progressBlock ? progressBlock : ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
|
||||
};
|
||||
delegate.completeBlock = ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[self finishSession:session];
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
QNResponseInfo *info = [[QNResponseInfo alloc] initWithResponseInfoHost:request.qn_domain response:httpResponse body:data error:error];
|
||||
completeBlock(info, info.responseDictionary);
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request];
|
||||
[uploadTask resume];
|
||||
}
|
||||
|
||||
- (void)multipartPost:(NSString *)url
|
||||
withData:(NSData *)data
|
||||
withParams:(NSDictionary *)params
|
||||
withFileName:(NSString *)key
|
||||
withMimeType:(NSString *)mime
|
||||
withIdentifier:(NSString *)identifier
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock
|
||||
withProgressBlock:(QNInternalProgressBlock)progressBlock
|
||||
withCancelBlock:(QNCancelBlock)cancelBlock
|
||||
withAccess:(NSString *)access {
|
||||
NSURL *URL = [[NSURL alloc] initWithString:url];
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
|
||||
request.HTTPMethod = @"POST";
|
||||
NSString *boundary = @"werghnvt54wef654rjuhgb56trtg34tweuyrgf";
|
||||
request.allHTTPHeaderFields = @{
|
||||
@"Content-Type" : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
|
||||
};
|
||||
NSMutableData *postData = [[NSMutableData alloc] init];
|
||||
for (NSString *paramsKey in params) {
|
||||
NSString *pair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n", boundary, paramsKey];
|
||||
[postData appendData:[pair dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
id value = [params objectForKey:paramsKey];
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
[postData appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
} else if ([value isKindOfClass:[NSData class]]) {
|
||||
[postData appendData:value];
|
||||
}
|
||||
[postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
NSString *filePair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\nContent-Type:%@\r\n\r\n", boundary, @"file", key, mime];
|
||||
[postData appendData:[filePair dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[postData appendData:data];
|
||||
[postData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
request.HTTPBody = postData;
|
||||
[request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)postData.length] forHTTPHeaderField:@"Content-Length"];
|
||||
|
||||
[self sendRequest:request withIdentifier:identifier withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock
|
||||
withAccess:access];
|
||||
}
|
||||
|
||||
- (void)post:(NSString *)url
|
||||
withData:(NSData *)data
|
||||
withParams:(NSDictionary *)params
|
||||
withHeaders:(NSDictionary *)headers
|
||||
withIdentifier:(NSString *)identifier
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock
|
||||
withProgressBlock:(QNInternalProgressBlock)progressBlock
|
||||
withCancelBlock:(QNCancelBlock)cancelBlock
|
||||
withAccess:(NSString *)access {
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:url]];
|
||||
if (headers) {
|
||||
[request setAllHTTPHeaderFields:headers];
|
||||
}
|
||||
[request setHTTPMethod:@"POST"];
|
||||
if (params) {
|
||||
[request setValuesForKeysWithDictionary:params];
|
||||
}
|
||||
[request setHTTPBody:data];
|
||||
identifier = !identifier ? [[NSUUID UUID] UUIDString] : identifier;
|
||||
QNAsyncRun(^{
|
||||
[self sendRequest:request
|
||||
withIdentifier:identifier
|
||||
withCompleteBlock:completeBlock
|
||||
withProgressBlock:progressBlock
|
||||
withCancelBlock:cancelBlock
|
||||
withAccess:access];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)get:(NSString *)url
|
||||
withHeaders:(NSDictionary *)headers
|
||||
withCompleteBlock:(QNCompleteBlock)completeBlock {
|
||||
QNAsyncRun(^{
|
||||
NSURL *URL = [NSURL URLWithString:url];
|
||||
|
||||
// NSString *domain = URL.host;
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
request.qn_domain = URL.host;
|
||||
QNSessionDelegateHandler *delegate = [[QNSessionDelegateHandler alloc] init];
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
__block NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:self.delegateQueue];
|
||||
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
|
||||
delegate.cancelBlock = nil;
|
||||
delegate.progressBlock = nil;
|
||||
delegate.completeBlock = ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[self finishSession:session];
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
QNResponseInfo *info = [[QNResponseInfo alloc] initWithResponseInfoHost:request.qn_domain response:httpResponse body:data error:error];
|
||||
completeBlock(info, info.responseDictionary);
|
||||
};
|
||||
[dataTask resume];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)finishSession:(NSURLSession *)session {
|
||||
[_lock lock];
|
||||
for (int i = 0; i < _sessionArray.count; i++) {
|
||||
NSDictionary *sessionInfo = _sessionArray[i];
|
||||
if (sessionInfo[@"session"] == session) {
|
||||
[session finishTasksAndInvalidate];
|
||||
[_sessionArray removeObject:sessionInfo];
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)invalidateSessionWithIdentifier:(NSString *)identifier {
|
||||
[_lock lock];
|
||||
for (int i = 0; i < _sessionArray.count; i++) {
|
||||
NSDictionary *sessionInfo = _sessionArray[i];
|
||||
if ([sessionInfo[@"identifier"] isEqualToString:identifier]) {
|
||||
NSURLSession *session = sessionInfo[@"session"];
|
||||
[session invalidateAndCancel];
|
||||
[_sessionArray removeObject:sessionInfo];
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
60
Pods/Qiniu/QiniuSDK/Collect/QNReportConfig.h
generated
Normal file
60
Pods/Qiniu/QiniuSDK/Collect/QNReportConfig.h
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// QNReportConfig.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by 杨森 on 2020/7/14.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNReportConfig : NSObject
|
||||
|
||||
- (id)init __attribute__((unavailable("Use sharedInstance: instead.")));
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* 是否开启sdk上传信息搜集 默认为YES
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isReportEnable) BOOL reportEnable;
|
||||
|
||||
/**
|
||||
* 每次上传时间间隔 单位:分钟 默认为0.5分钟
|
||||
*/
|
||||
@property (nonatomic, assign) double interval;
|
||||
|
||||
/**
|
||||
* 记录文件大于 uploadThreshold 会触发上传,单位:字节 默认为16 * 1024
|
||||
*/
|
||||
@property (nonatomic, assign) uint64_t uploadThreshold;
|
||||
|
||||
/**
|
||||
* 记录文件最大值 要大于 uploadThreshold 单位:字节 默认为20 * 1024 * 1024
|
||||
*/
|
||||
@property (nonatomic, assign) uint64_t maxRecordFileSize;
|
||||
|
||||
/**
|
||||
* 记录文件所在文件夹目录 默认为:.../沙盒/Library/Caches/com.qiniu.report
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *recordDirectory;
|
||||
|
||||
/**
|
||||
* 信息上报服务器地址
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *serverURL;
|
||||
|
||||
/**
|
||||
* 信息上报服务器地址 host
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *serverHost;
|
||||
|
||||
/**
|
||||
* 信息上报请求超时时间 单位:秒 默认为10秒
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) NSTimeInterval timeoutInterval;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
42
Pods/Qiniu/QiniuSDK/Collect/QNReportConfig.m
generated
Normal file
42
Pods/Qiniu/QiniuSDK/Collect/QNReportConfig.m
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// QNReportConfig.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by 杨森 on 2020/7/14.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNReportConfig.h"
|
||||
#import "QNConfig.h"
|
||||
#import "QNUtils.h"
|
||||
|
||||
@implementation QNReportConfig
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
|
||||
static QNReportConfig *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_reportEnable = YES;
|
||||
_interval = 0.5;
|
||||
_serverHost = kQNUpLogHost;
|
||||
_recordDirectory = [NSString stringWithFormat:@"%@/report", [QNUtils sdkCacheDirectory]];
|
||||
_maxRecordFileSize = 20 * 1024 * 1024;
|
||||
_uploadThreshold = 16 * 1024;
|
||||
_timeoutInterval = 10;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)serverURL {
|
||||
return [NSString stringWithFormat:@"https://%@/log/4?compressed=gzip", _serverHost];
|
||||
}
|
||||
@end
|
||||
143
Pods/Qiniu/QiniuSDK/Collect/QNReportItem.h
generated
Normal file
143
Pods/Qiniu/QiniuSDK/Collect/QNReportItem.h
generated
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// QNReportItem.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/5/12.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadInfoReporter.h"
|
||||
#import "QNResponseInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNReportItem : NSObject
|
||||
|
||||
+ (instancetype)item;
|
||||
|
||||
/// 设置打点日志字段
|
||||
/// @param value log value
|
||||
/// @param key log key
|
||||
- (void)setReportValue:(id _Nullable)value forKey:(NSString * _Nullable)key;
|
||||
|
||||
/// 移除打点日志字段
|
||||
/// @param key log key
|
||||
- (void)removeReportValueForKey:(NSString * _Nullable)key;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadInfoReporter(ReportItem)
|
||||
|
||||
- (void)reportItem:(QNReportItem *)item token:(NSString *)token;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNResponseInfo(Report)
|
||||
|
||||
@property(nonatomic, assign, readonly)NSNumber *requestReportStatusCode;
|
||||
@property(nonatomic, copy, readonly)NSString *requestReportErrorType;
|
||||
|
||||
@property(nonatomic, copy, readonly)NSString *qualityResult;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
//MARK:-- 日志类型
|
||||
extern NSString *const QNReportLogTypeRequest;
|
||||
extern NSString *const QNReportLogTypeBlock;
|
||||
extern NSString *const QNReportLogTypeQuality;
|
||||
|
||||
//MARK:-- 请求信息打点⽇志
|
||||
extern NSString *const QNReportRequestKeyLogType;
|
||||
extern NSString *const QNReportRequestKeyUpTime;
|
||||
extern NSString *const QNReportRequestKeyStatusCode;
|
||||
extern NSString *const QNReportRequestKeyRequestId;
|
||||
extern NSString *const QNReportRequestKeyHost;
|
||||
extern NSString *const QNReportRequestKeyHttpVersion;
|
||||
extern NSString *const QNReportRequestKeyRemoteIp;
|
||||
extern NSString *const QNReportRequestKeyPort;
|
||||
extern NSString *const QNReportRequestKeyTargetBucket;
|
||||
extern NSString *const QNReportRequestKeyTargetKey;
|
||||
extern NSString *const QNReportRequestKeyTotalElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyDnsElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyConnectElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyTLSConnectElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyRequestElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyWaitElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyResponseElapsedTime;
|
||||
extern NSString *const QNReportRequestKeyFileOffset;
|
||||
extern NSString *const QNReportRequestKeyBytesSent;
|
||||
extern NSString *const QNReportRequestKeyBytesTotal;
|
||||
extern NSString *const QNReportRequestKeyPid;
|
||||
extern NSString *const QNReportRequestKeyTid;
|
||||
extern NSString *const QNReportRequestKeyTargetRegionId;
|
||||
extern NSString *const QNReportRequestKeyCurrentRegionId;
|
||||
extern NSString *const QNReportRequestKeyErrorType;
|
||||
extern NSString *const QNReportRequestKeyErrorDescription;
|
||||
extern NSString *const QNReportRequestKeyUpType;
|
||||
extern NSString *const QNReportRequestKeyOsName;
|
||||
extern NSString *const QNReportRequestKeyOsVersion;
|
||||
extern NSString *const QNReportRequestKeySDKName;
|
||||
extern NSString *const QNReportRequestKeySDKVersion;
|
||||
extern NSString *const QNReportRequestKeyClientTime;
|
||||
extern NSString *const QNReportRequestKeyHttpClient;
|
||||
extern NSString *const QNReportRequestKeyNetworkType;
|
||||
extern NSString *const QNReportRequestKeySignalStrength;
|
||||
extern NSString *const QNReportRequestKeyPrefetchedDnsSource;
|
||||
extern NSString *const QNReportRequestKeyDnsSource;
|
||||
extern NSString *const QNReportRequestKeyDnsErrorMessage;
|
||||
extern NSString *const QNReportRequestKeyPrefetchedBefore;
|
||||
extern NSString *const QNReportRequestKeyPrefetchedErrorMessage;
|
||||
extern NSString *const QNReportRequestKeyNetworkMeasuring;
|
||||
extern NSString *const QNReportRequestKeyPerceptiveSpeed;
|
||||
extern NSString *const QNReportRequestKeyHijacking;
|
||||
|
||||
//MARK:-- 分块上传统计⽇志
|
||||
extern NSString *const QNReportBlockKeyLogType;
|
||||
extern NSString *const QNReportBlockKeyUpTime;
|
||||
extern NSString *const QNReportBlockKeyTargetBucket;
|
||||
extern NSString *const QNReportBlockKeyTargetKey;
|
||||
extern NSString *const QNReportBlockKeyTargetRegionId;
|
||||
extern NSString *const QNReportBlockKeyCurrentRegionId;
|
||||
extern NSString *const QNReportBlockKeyTotalElapsedTime;
|
||||
extern NSString *const QNReportBlockKeyBytesSent;
|
||||
extern NSString *const QNReportBlockKeyRecoveredFrom;
|
||||
extern NSString *const QNReportBlockKeyFileSize;
|
||||
extern NSString *const QNReportBlockKeyPid;
|
||||
extern NSString *const QNReportBlockKeyTid;
|
||||
extern NSString *const QNReportBlockKeyUpApiVersion;
|
||||
extern NSString *const QNReportBlockKeyClientTime;
|
||||
extern NSString *const QNReportBlockKeyOsName;
|
||||
extern NSString *const QNReportBlockKeyOsVersion;
|
||||
extern NSString *const QNReportBlockKeySDKName;
|
||||
extern NSString *const QNReportBlockKeySDKVersion;
|
||||
extern NSString *const QNReportBlockKeyPerceptiveSpeed;
|
||||
extern NSString *const QNReportBlockKeyHijacking;
|
||||
|
||||
//MARK:-- 上传质量统计
|
||||
extern NSString *const QNReportQualityKeyLogType;
|
||||
extern NSString *const QNReportQualityKeyUpType;
|
||||
extern NSString *const QNReportQualityKeyUpTime;
|
||||
extern NSString *const QNReportQualityKeyResult;
|
||||
extern NSString *const QNReportQualityKeyTargetBucket;
|
||||
extern NSString *const QNReportQualityKeyTargetKey;
|
||||
extern NSString *const QNReportQualityKeyTotalElapsedTime;
|
||||
extern NSString *const QNReportQualityKeyUcQueryElapsedTime;
|
||||
extern NSString *const QNReportQualityKeyRequestsCount;
|
||||
extern NSString *const QNReportQualityKeyRegionsCount;
|
||||
extern NSString *const QNReportQualityKeyBytesSent;
|
||||
extern NSString *const QNReportQualityKeyFileSize;
|
||||
extern NSString *const QNReportQualityKeyCloudType;
|
||||
extern NSString *const QNReportQualityKeyErrorType;
|
||||
extern NSString *const QNReportQualityKeyErrorDescription;
|
||||
extern NSString *const QNReportQualityKeyOsName;
|
||||
extern NSString *const QNReportQualityKeyOsVersion;
|
||||
extern NSString *const QNReportQualityKeySDKName;
|
||||
extern NSString *const QNReportQualityKeySDKVersion;
|
||||
extern NSString *const QNReportQualityKeyPerceptiveSpeed;
|
||||
extern NSString *const QNReportQualityKeyHijacking;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
257
Pods/Qiniu/QiniuSDK/Collect/QNReportItem.m
generated
Normal file
257
Pods/Qiniu/QiniuSDK/Collect/QNReportItem.m
generated
Normal file
@@ -0,0 +1,257 @@
|
||||
//
|
||||
// QNReportItem.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/5/12.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNReportItem.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNLogUtil.h"
|
||||
|
||||
@interface QNReportItem()
|
||||
|
||||
@property(nonatomic, strong)NSMutableDictionary *keyValues;
|
||||
|
||||
@end
|
||||
@implementation QNReportItem
|
||||
|
||||
+ (instancetype)item{
|
||||
QNReportItem *item = [[QNReportItem alloc] init];
|
||||
return item;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initData{
|
||||
_keyValues = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
- (void)setReportValue:(id _Nullable)value forKey:(NSString * _Nullable)key{
|
||||
if (!value || !key || ![key isKindOfClass:[NSString class]]) {
|
||||
return;
|
||||
}
|
||||
if ([value isKindOfClass:[NSString class]] && [(NSString *)value length] > 1024) {
|
||||
value = [(NSString *)value substringToIndex:1024];
|
||||
}
|
||||
[self.keyValues setValue:value forKey:key];
|
||||
}
|
||||
|
||||
- (void)removeReportValueForKey:(NSString * _Nullable)key{
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
[self.keyValues removeObjectForKey:key];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)toJson{
|
||||
|
||||
NSString *jsonString = @"{}";
|
||||
if (!self.keyValues || self.keyValues.count == 0) {
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self.keyValues
|
||||
options:NSJSONWritingFragmentsAllowed
|
||||
error:nil];
|
||||
if (!jsonData) {
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNUploadInfoReporter(ReportItem)
|
||||
|
||||
- (void)reportItem:(QNReportItem *)item token:(NSString *)token{
|
||||
NSString *itemJsonString = [item toJson];
|
||||
QNLogInfo(@"up log:%@", itemJsonString);
|
||||
if (itemJsonString && ![itemJsonString isEqualToString:@"{}"]) {
|
||||
[kQNReporter report:itemJsonString token:token];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNResponseInfo(Report)
|
||||
|
||||
- (NSNumber *)requestReportStatusCode{
|
||||
return @(self.statusCode);
|
||||
}
|
||||
|
||||
- (NSString *)requestReportErrorType{
|
||||
NSString *errorType = nil;
|
||||
if (self.statusCode == -1){
|
||||
errorType = @"network_error";
|
||||
} else if (self.statusCode == kQNLocalIOError){
|
||||
errorType = @"local_io_error";
|
||||
} else if (self.statusCode == 100){
|
||||
errorType = @"protocol_error";
|
||||
} else if (self.statusCode > 199 && self.statusCode < 300) {
|
||||
// NSURLErrorFailingURLErrorKey
|
||||
} else if (self.statusCode > 299){
|
||||
errorType = @"response_error";
|
||||
} else if (self.statusCode == -1003){
|
||||
errorType = @"unknown_host";
|
||||
} else if (self.statusCode == -1009){
|
||||
errorType = @"network_slow";
|
||||
} else if (self.statusCode == -1001){
|
||||
errorType = @"timeout";
|
||||
} else if (self.statusCode == -1004){
|
||||
errorType = @"cannot_connect_to_host";
|
||||
} else if (self.statusCode == -1005 || self.statusCode == -1021){
|
||||
errorType = @"transmission_error";
|
||||
} else if ((self.statusCode <= -1200 && self.statusCode >= -1206) || self.statusCode == -2000 || self.statusCode == -9807){
|
||||
errorType = @"ssl_error";
|
||||
} else if (self.statusCode == -1015 || self.statusCode == -1016 || self.statusCode == -1017){
|
||||
errorType = @"parse_error";
|
||||
} else if (self.statusCode == -1007 || self.statusCode == -1010 || self.statusCode == kQNMaliciousResponseError){
|
||||
errorType = @"malicious_response";
|
||||
} else if (self.statusCode == kQNUnexpectedSysCallError
|
||||
|| (self.statusCode > -1130 && self.statusCode <= -1010)){
|
||||
errorType = @"unexpected_syscall_error";
|
||||
} else if (self.statusCode == kQNRequestCancelled
|
||||
|| self.statusCode == NSURLErrorCancelled){
|
||||
errorType = @"user_canceled";
|
||||
} else {
|
||||
errorType = @"unknown_error";
|
||||
}
|
||||
return errorType;
|
||||
}
|
||||
|
||||
- (NSString *)qualityResult{
|
||||
|
||||
NSString *result = nil;
|
||||
|
||||
if (self.statusCode > 199 && self.statusCode < 300) {
|
||||
result = @"ok";
|
||||
} else if (self.statusCode > 399 &&
|
||||
(self.statusCode < 500 || self.statusCode == 573 || self.statusCode == 579 ||
|
||||
self.statusCode == 608 || self.statusCode == 612 || self.statusCode == 614 || self.statusCode == 630 || self.statusCode == 631 ||
|
||||
self.statusCode == 701)) {
|
||||
result = @"bad_request";
|
||||
} else if (self.statusCode == kQNZeroDataSize){
|
||||
result = @"zero_size_file";
|
||||
} else if (self.statusCode == kQNFileError){
|
||||
result = @"invalid_file";
|
||||
} else if (self.statusCode == kQNInvalidToken
|
||||
|| self.statusCode == kQNInvalidArgument){
|
||||
result = @"invalid_args";
|
||||
}
|
||||
|
||||
if (result == nil) {
|
||||
result = [self requestReportErrorType];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//MARK:-- 日志类型
|
||||
NSString * const QNReportLogTypeRequest = @"request";
|
||||
NSString * const QNReportLogTypeBlock = @"block";
|
||||
NSString * const QNReportLogTypeQuality = @"quality";
|
||||
|
||||
|
||||
//MARK:-- 请求信息打点⽇志
|
||||
NSString * const QNReportRequestKeyLogType = @"log_type";
|
||||
NSString * const QNReportRequestKeyUpTime = @"up_time";
|
||||
NSString * const QNReportRequestKeyStatusCode = @"status_code";
|
||||
NSString * const QNReportRequestKeyRequestId = @"req_id";
|
||||
NSString * const QNReportRequestKeyHost = @"host";
|
||||
NSString * const QNReportRequestKeyHttpVersion = @"http_version";
|
||||
NSString * const QNReportRequestKeyRemoteIp = @"remote_ip";
|
||||
NSString * const QNReportRequestKeyPort = @"port";
|
||||
NSString * const QNReportRequestKeyTargetBucket = @"target_bucket";
|
||||
NSString * const QNReportRequestKeyTargetKey = @"target_key";
|
||||
NSString * const QNReportRequestKeyTotalElapsedTime = @"total_elapsed_time";
|
||||
NSString * const QNReportRequestKeyDnsElapsedTime = @"dns_elapsed_time";
|
||||
NSString * const QNReportRequestKeyConnectElapsedTime = @"connect_elapsed_time";
|
||||
NSString * const QNReportRequestKeyTLSConnectElapsedTime = @"tls_connect_elapsed_time";
|
||||
NSString * const QNReportRequestKeyRequestElapsedTime = @"request_elapsed_time";
|
||||
NSString * const QNReportRequestKeyWaitElapsedTime = @"wait_elapsed_time";
|
||||
NSString * const QNReportRequestKeyResponseElapsedTime = @"response_elapsed_time";
|
||||
NSString * const QNReportRequestKeyFileOffset = @"file_offset";
|
||||
NSString * const QNReportRequestKeyBytesSent = @"bytes_sent";
|
||||
NSString * const QNReportRequestKeyBytesTotal = @"bytes_total";
|
||||
NSString * const QNReportRequestKeyPid = @"pid";
|
||||
NSString * const QNReportRequestKeyTid = @"tid";
|
||||
NSString * const QNReportRequestKeyTargetRegionId = @"target_region_id";
|
||||
NSString * const QNReportRequestKeyCurrentRegionId = @"current_region_id";
|
||||
NSString * const QNReportRequestKeyErrorType = @"error_type";
|
||||
NSString * const QNReportRequestKeyErrorDescription = @"error_description";
|
||||
NSString * const QNReportRequestKeyUpType = @"up_type";
|
||||
NSString * const QNReportRequestKeyOsName = @"os_name";
|
||||
NSString * const QNReportRequestKeyOsVersion = @"os_version";
|
||||
NSString * const QNReportRequestKeySDKName = @"sdk_name";
|
||||
NSString * const QNReportRequestKeySDKVersion = @"sdk_version";
|
||||
NSString * const QNReportRequestKeyClientTime = @"client_time";
|
||||
NSString * const QNReportRequestKeyHttpClient = @"http_client";
|
||||
NSString * const QNReportRequestKeyNetworkType = @"network_type";
|
||||
NSString * const QNReportRequestKeySignalStrength = @"signal_strength";
|
||||
NSString * const QNReportRequestKeyPrefetchedDnsSource = @"prefetched_dns_source";
|
||||
NSString * const QNReportRequestKeyDnsSource = @"dns_source";
|
||||
NSString * const QNReportRequestKeyDnsErrorMessage = @"dns_error_message";
|
||||
NSString * const QNReportRequestKeyPrefetchedBefore = @"prefetched_before";
|
||||
NSString * const QNReportRequestKeyPrefetchedErrorMessage = @"prefetched_error_message";
|
||||
NSString * const QNReportRequestKeyNetworkMeasuring = @"network_measuring";
|
||||
NSString * const QNReportRequestKeyPerceptiveSpeed = @"perceptive_speed";
|
||||
NSString * const QNReportRequestKeyHijacking = @"hijacking";
|
||||
|
||||
//MARK:-- 分块上传统计⽇志
|
||||
NSString * const QNReportBlockKeyLogType = @"log_type";
|
||||
NSString * const QNReportBlockKeyUpTime = @"up_time";
|
||||
NSString * const QNReportBlockKeyTargetBucket = @"target_bucket";
|
||||
NSString * const QNReportBlockKeyTargetKey = @"target_key";
|
||||
NSString * const QNReportBlockKeyTargetRegionId = @"target_region_id";
|
||||
NSString * const QNReportBlockKeyCurrentRegionId = @"current_region_id";
|
||||
NSString * const QNReportBlockKeyTotalElapsedTime = @"total_elapsed_time";
|
||||
NSString * const QNReportBlockKeyBytesSent = @"bytes_sent";
|
||||
NSString * const QNReportBlockKeyRecoveredFrom = @"recovered_from";
|
||||
NSString * const QNReportBlockKeyFileSize = @"file_size";
|
||||
NSString * const QNReportBlockKeyPid = @"pid";
|
||||
NSString * const QNReportBlockKeyTid = @"tid";
|
||||
NSString * const QNReportBlockKeyUpApiVersion = @"up_api_version";
|
||||
NSString * const QNReportBlockKeyClientTime = @"client_time";
|
||||
NSString * const QNReportBlockKeyOsName = @"os_name";
|
||||
NSString * const QNReportBlockKeyOsVersion = @"os_version";
|
||||
NSString * const QNReportBlockKeySDKName = @"sdk_name";
|
||||
NSString * const QNReportBlockKeySDKVersion = @"sdk_version";
|
||||
NSString * const QNReportBlockKeyPerceptiveSpeed = @"perceptive_speed";
|
||||
NSString * const QNReportBlockKeyHijacking = @"hijacking";
|
||||
|
||||
|
||||
//MARK:-- 上传质量统计
|
||||
NSString * const QNReportQualityKeyLogType = @"log_type";
|
||||
NSString * const QNReportQualityKeyUpType = @"up_type";
|
||||
NSString * const QNReportQualityKeyUpTime = @"up_time";
|
||||
NSString * const QNReportQualityKeyResult = @"result";
|
||||
NSString * const QNReportQualityKeyTargetBucket = @"target_bucket";
|
||||
NSString * const QNReportQualityKeyTargetKey = @"target_key";
|
||||
NSString * const QNReportQualityKeyTotalElapsedTime = @"total_elapsed_time";
|
||||
NSString * const QNReportQualityKeyUcQueryElapsedTime = @"uc_query_elapsed_time";
|
||||
NSString * const QNReportQualityKeyRequestsCount = @"requests_count";
|
||||
NSString * const QNReportQualityKeyRegionsCount = @"regions_count";
|
||||
NSString * const QNReportQualityKeyBytesSent = @"bytes_sent";
|
||||
NSString * const QNReportQualityKeyFileSize = @"file_size";
|
||||
NSString * const QNReportQualityKeyCloudType = @"cloud_type";
|
||||
NSString * const QNReportQualityKeyErrorType = @"error_type";
|
||||
NSString * const QNReportQualityKeyErrorDescription = @"error_description";
|
||||
NSString * const QNReportQualityKeyOsName = @"os_name";
|
||||
NSString * const QNReportQualityKeyOsVersion = @"os_version";
|
||||
NSString * const QNReportQualityKeySDKName = @"sdk_name";
|
||||
NSString * const QNReportQualityKeySDKVersion = @"sdk_version";
|
||||
NSString * const QNReportQualityKeyPerceptiveSpeed = @"perceptive_speed";
|
||||
NSString * const QNReportQualityKeyHijacking = @"hijacking";
|
||||
36
Pods/Qiniu/QiniuSDK/Collect/QNUploadInfoReporter.h
generated
Normal file
36
Pods/Qiniu/QiniuSDK/Collect/QNUploadInfoReporter.h
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// QNUploadInfoReporter.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2019/6/24.
|
||||
// Copyright © 2019 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define kQNReporter [QNUploadInfoReporter sharedInstance]
|
||||
@interface QNUploadInfoReporter : NSObject
|
||||
|
||||
- (id)init __attribute__((unavailable("Use sharedInstance: instead.")));
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* 上报统计信息
|
||||
*
|
||||
* @param jsonString 需要记录的json字符串
|
||||
* @param token 上传凭证
|
||||
*
|
||||
*/
|
||||
- (void)report:(NSString *)jsonString token:(NSString *)token;
|
||||
|
||||
/**
|
||||
* 清空统计信息
|
||||
*/
|
||||
- (void)clean;
|
||||
|
||||
@end
|
||||
|
||||
242
Pods/Qiniu/QiniuSDK/Collect/QNUploadInfoReporter.m
generated
Normal file
242
Pods/Qiniu/QiniuSDK/Collect/QNUploadInfoReporter.m
generated
Normal file
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// QNUploadInfoReporter.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2019/6/24.
|
||||
// Copyright © 2019 Qiniu. All rights reserved.
|
||||
//
|
||||
#import "QNDefine.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUploadInfoReporter.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNFile.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNUserAgent.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNVersion.h"
|
||||
#import "QNReportConfig.h"
|
||||
#import "NSData+QNGZip.h"
|
||||
#import "QNTransactionManager.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
|
||||
#define kQNUplogDelayReportTransactionName @"com.qiniu.uplog"
|
||||
|
||||
@interface QNUploadInfoReporter ()
|
||||
|
||||
@property (nonatomic, strong) QNReportConfig *config;
|
||||
@property (nonatomic, assign) NSTimeInterval lastReportTime;
|
||||
@property (nonatomic, strong) NSString *recorderFilePath;
|
||||
@property (nonatomic, strong) NSString *recorderTempFilePath;
|
||||
@property (nonatomic, copy) NSString *X_Log_Client_Id;
|
||||
|
||||
@property (nonatomic, strong) QNRequestTransaction *transaction;
|
||||
@property (nonatomic, assign) BOOL isReporting;
|
||||
|
||||
@property (nonatomic, strong) dispatch_queue_t recordQueue;
|
||||
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
|
||||
@end
|
||||
|
||||
@implementation QNUploadInfoReporter
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
|
||||
static QNUploadInfoReporter *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_config = [QNReportConfig sharedInstance];
|
||||
_lastReportTime = 0;
|
||||
_recorderFilePath = [NSString stringWithFormat:@"%@/%@", _config.recordDirectory, @"qiniu.log"];
|
||||
_recorderTempFilePath = [NSString stringWithFormat:@"%@/%@", _config.recordDirectory, @"qiniuTemp.log"];
|
||||
_recordQueue = dispatch_queue_create("com.qiniu.reporter", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)clean {
|
||||
[self cleanRecorderFile];
|
||||
[self cleanTempRecorderFile];
|
||||
}
|
||||
|
||||
- (void)cleanRecorderFile {
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
if ([manager fileExistsAtPath:_recorderFilePath]) {
|
||||
NSError *error = nil;
|
||||
[manager removeItemAtPath:_recorderFilePath error:&error];
|
||||
if (error) {
|
||||
NSLog(@"remove recorder file failed: %@", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cleanTempRecorderFile {
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
if ([manager fileExistsAtPath:_recorderTempFilePath]) {
|
||||
NSError *error = nil;
|
||||
[manager removeItemAtPath:_recorderTempFilePath error:&error];
|
||||
if (error) {
|
||||
NSLog(@"remove recorder temp file failed: %@", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)checkReportAvailable {
|
||||
if (!_config.isReportEnable) {
|
||||
return NO;
|
||||
}
|
||||
if (_config.maxRecordFileSize <= _config.uploadThreshold) {
|
||||
NSLog(@"maxRecordFileSize must be larger than uploadThreshold");
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)report:(NSString *)jsonString token:(NSString *)token {
|
||||
|
||||
if (![self checkReportAvailable] || !jsonString || !token || token.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 串行队列处理文件读写
|
||||
dispatch_async(self.recordQueue, ^{
|
||||
[kQNReporter saveReportJsonString:jsonString];
|
||||
[kQNReporter reportToServerIfNeeded:token];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)saveReportJsonString:(NSString *)jsonString {
|
||||
NSString *finalRecordInfo = [jsonString stringByAppendingString:@"\n"];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if (![fileManager fileExistsAtPath:self.recorderFilePath]) {
|
||||
// 如果recordFile不存在,创建文件并写入首行,首次不上传
|
||||
[finalRecordInfo writeToFile:_recorderFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
} else {
|
||||
NSDictionary *recorderFileAttr = [fileManager attributesOfItemAtPath:self.recorderFilePath error:nil];
|
||||
if ([recorderFileAttr fileSize] > self.config.maxRecordFileSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSFileHandle *fileHandler = nil;
|
||||
@try {
|
||||
// 上传信息写入recorder文件
|
||||
fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:_recorderFilePath];
|
||||
[fileHandler seekToEndOfFile];
|
||||
[fileHandler writeData: [finalRecordInfo dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"NSFileHandle cannot write data: %@", exception.description);
|
||||
} @finally {
|
||||
[fileHandler closeFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reportToServerIfNeeded:(NSString *)tokenString {
|
||||
BOOL needToReport = NO;
|
||||
long currentTime = [[NSDate date] timeIntervalSince1970];
|
||||
long interval = self.config.interval * 10;
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSDictionary *recorderFileAttr = [fileManager attributesOfItemAtPath:self.recorderFilePath error:nil];
|
||||
if ([fileManager fileExistsAtPath:self.recorderTempFilePath]) {
|
||||
needToReport = YES;
|
||||
} else if ((self.lastReportTime == 0 || (currentTime - self.lastReportTime) >= interval || [recorderFileAttr fileSize] > self.config.uploadThreshold) &&
|
||||
([fileManager moveItemAtPath:self.recorderFilePath toPath:self.recorderTempFilePath error:nil])) {
|
||||
needToReport = YES;
|
||||
}
|
||||
|
||||
if (needToReport && !self.isReporting) {
|
||||
[self reportToServer:tokenString];
|
||||
} else {
|
||||
// 有未上传日志存在,则 interval 时间后再次重试一次
|
||||
if (![fileManager fileExistsAtPath:self.recorderFilePath] || [recorderFileAttr fileSize] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *transactionList = [kQNTransactionManager transactionsForName:kQNUplogDelayReportTransactionName];
|
||||
if (transactionList != nil && transactionList.count > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transactionList != nil && transactionList.count == 1) {
|
||||
QNTransaction *transaction = transactionList.firstObject;
|
||||
if (transaction != nil && !transaction.isExecuting) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QNTransaction *transaction = [QNTransaction transaction:kQNUplogDelayReportTransactionName after:interval action:^{
|
||||
[kQNReporter reportToServerIfNeeded:tokenString];
|
||||
}];
|
||||
[kQNTransactionManager addTransaction:transaction];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)reportToServer:(NSString *)tokenString {
|
||||
if (tokenString == nil) {
|
||||
return;
|
||||
}
|
||||
QNUpToken *token = [QNUpToken parse:tokenString];
|
||||
if (!token.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *logData = [self getLogData];
|
||||
if (logData == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isReporting = YES;
|
||||
logData = [NSData qn_gZip:logData];
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
|
||||
[transaction reportLog:logData logClientId:self.X_Log_Client_Id complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
if (responseInfo.isOK) {
|
||||
self.lastReportTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970];
|
||||
if (!self.X_Log_Client_Id) {
|
||||
self.X_Log_Client_Id = responseInfo.responseHeader[@"x-log-client-id"];
|
||||
}
|
||||
[self cleanTempRecorderFile];
|
||||
} else {
|
||||
NSLog(@"upload info report failed: %@", responseInfo);
|
||||
}
|
||||
|
||||
self.isReporting = NO;
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSData *)getLogData {
|
||||
return [NSData dataWithContentsOfFile:_recorderTempFilePath];
|
||||
}
|
||||
|
||||
- (QNRequestTransaction *)createUploadRequestTransaction:(QNUpToken *)token{
|
||||
if (self.config.serverURL) {
|
||||
|
||||
}
|
||||
NSArray *hosts = nil;
|
||||
if (self.config.serverHost) {
|
||||
hosts = @[self.config.serverHost];
|
||||
}
|
||||
QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithHosts:hosts
|
||||
regionId:QNZoneInfoEmptyRegionId
|
||||
token:token];
|
||||
self.transaction = transaction;
|
||||
return transaction;
|
||||
}
|
||||
|
||||
- (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction{
|
||||
self.transaction = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
27
Pods/Qiniu/QiniuSDK/Common/QNAutoZone.h
generated
Normal file
27
Pods/Qiniu/QiniuSDK/Common/QNAutoZone.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// QNAutoZone.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNZone.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNFixedZone;
|
||||
@interface QNAutoZone : QNZone
|
||||
|
||||
+ (instancetype)zoneWithUcHosts:(NSArray *)ucHosts;
|
||||
|
||||
+ (void)clearCache;
|
||||
|
||||
/**
|
||||
* 当 查询失败时,会使用 zones 进行上传,默认不配置。
|
||||
*/
|
||||
- (void)setDefaultZones:(NSArray <QNFixedZone *> *)zones;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
254
Pods/Qiniu/QiniuSDK/Common/QNAutoZone.m
generated
Normal file
254
Pods/Qiniu/QiniuSDK/Common/QNAutoZone.m
generated
Normal file
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// QNAutoZone.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNCache.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNAutoZone.h"
|
||||
#import "QNConfig.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNFixedZone.h"
|
||||
#import "QNSingleFlight.h"
|
||||
#import "QNFileRecorder.h"
|
||||
#import "QNUrlSafeBase64.h"
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
|
||||
@interface QNUCQuerySingleFlightValue : NSObject
|
||||
|
||||
@property(nonatomic, strong) QNResponseInfo *responseInfo;
|
||||
@property(nonatomic, strong) NSDictionary *response;
|
||||
@property(nonatomic, strong) QNUploadRegionRequestMetrics *metrics;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNUCQuerySingleFlightValue
|
||||
@end
|
||||
|
||||
@interface QNAutoZone ()
|
||||
|
||||
@property(nonatomic, strong) NSArray *ucHosts;
|
||||
@property(nonatomic, strong) QNFixedZone *defaultZone;
|
||||
// 已经查询到的区域信息
|
||||
@property(nonatomic, strong) NSMutableDictionary <NSString *, QNZonesInfo *> *zonesDic;
|
||||
@property(nonatomic, strong) NSMutableArray <QNRequestTransaction *> *transactions;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNAutoZone
|
||||
|
||||
+ (QNSingleFlight *)UCQuerySingleFlight {
|
||||
static QNSingleFlight *singleFlight = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
singleFlight = [[QNSingleFlight alloc] init];
|
||||
});
|
||||
return singleFlight;
|
||||
}
|
||||
|
||||
+ (instancetype)zoneWithUcHosts:(NSArray *)ucHosts {
|
||||
QNAutoZone *zone = [[self alloc] init];
|
||||
zone.ucHosts = [ucHosts copy];
|
||||
return zone;
|
||||
}
|
||||
|
||||
+ (QNCache *)zoneShareCache {
|
||||
static QNCache *queryCache;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
QNCacheOption *option = [[QNCacheOption alloc] init];
|
||||
option.version = @"v2";
|
||||
queryCache = [QNCache cache:[QNZonesInfo class] option:option];
|
||||
});
|
||||
return queryCache;
|
||||
}
|
||||
|
||||
+ (void)clearCache {
|
||||
[[QNAutoZone zoneShareCache] clearMemoryCache];
|
||||
[[QNAutoZone zoneShareCache] clearDiskCache];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_zonesDic = [NSMutableDictionary dictionary];
|
||||
_transactions = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setDefaultZones:(NSArray <QNFixedZone *> *)zones {
|
||||
self.defaultZone = [QNFixedZone combineZones:zones];
|
||||
}
|
||||
|
||||
- (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken *_Nullable)token {
|
||||
|
||||
if (token == nil) return nil;
|
||||
NSString *cacheKey = [self makeCacheKey:[QNConfiguration defaultConfiguration] akAndBucket:token.index];
|
||||
QNZonesInfo *zonesInfo = nil;
|
||||
@synchronized (self) {
|
||||
zonesInfo = self.zonesDic[cacheKey];
|
||||
}
|
||||
zonesInfo = [zonesInfo copy];
|
||||
return zonesInfo;
|
||||
}
|
||||
|
||||
- (void)setZonesInfo:(QNZonesInfo *)info forKey:(NSString *)key {
|
||||
if (info == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
self.zonesDic[key] = info;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)preQuery:(QNUpToken *)token on:(QNPrequeryReturn)ret {
|
||||
[self query:[QNConfiguration defaultConfiguration] token:token on:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, QNZonesInfo * _Nullable zonesInfo) {
|
||||
if (!ret) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseInfo.isOK) {
|
||||
ret(0, responseInfo, metrics);
|
||||
} else {
|
||||
ret(responseInfo.statusCode, responseInfo, metrics);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)query:(QNConfiguration *)config token:(QNUpToken *)token on:(QNQueryReturn)ret {
|
||||
if (token == nil || ![token isValid]) {
|
||||
ret([QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
QNUploadRegionRequestMetrics *cacheMetrics = [QNUploadRegionRequestMetrics emptyMetrics];
|
||||
[cacheMetrics start];
|
||||
|
||||
|
||||
NSString *cacheKey = [self makeCacheKey:config akAndBucket:token.index];
|
||||
QNZonesInfo *zonesInfo = [[QNAutoZone zoneShareCache] cacheForKey:cacheKey];
|
||||
|
||||
// 临时的 zonesInfo 仅能使用一次
|
||||
if (zonesInfo != nil && zonesInfo.isValid && !zonesInfo.isTemporary) {
|
||||
[cacheMetrics end];
|
||||
[self setZonesInfo:zonesInfo forKey:cacheKey];
|
||||
ret([QNResponseInfo successResponse], cacheMetrics, zonesInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
QNSingleFlight *singleFlight = [QNAutoZone UCQuerySingleFlight];
|
||||
[singleFlight perform:token.index action:^(QNSingleFlightComplete _Nonnull complete) {
|
||||
kQNStrongSelf;
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction:config token:token];
|
||||
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction queryUploadHosts:^(QNResponseInfo *_Nullable responseInfo, QNUploadRegionRequestMetrics *_Nullable metrics, NSDictionary *_Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
|
||||
QNUCQuerySingleFlightValue *value = [[QNUCQuerySingleFlightValue alloc] init];
|
||||
value.responseInfo = responseInfo;
|
||||
value.response = response;
|
||||
value.metrics = metrics;
|
||||
complete(value, nil);
|
||||
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
|
||||
} complete:^(id _Nullable value, NSError *_Nullable error) {
|
||||
kQNStrongSelf;
|
||||
|
||||
QNResponseInfo *responseInfo = [(QNUCQuerySingleFlightValue *) value responseInfo];
|
||||
NSDictionary *response = [(QNUCQuerySingleFlightValue *) value response];
|
||||
QNUploadRegionRequestMetrics *metrics = [(QNUCQuerySingleFlightValue *) value metrics];
|
||||
|
||||
if (responseInfo && responseInfo.isOK) {
|
||||
QNZonesInfo *zonesInfo = [QNZonesInfo infoWithDictionary:response];
|
||||
if ([zonesInfo isValid]) {
|
||||
[self setZonesInfo:zonesInfo forKey:cacheKey];
|
||||
[[QNAutoZone zoneShareCache] cache:zonesInfo forKey:cacheKey atomically:false];
|
||||
ret(responseInfo, metrics, zonesInfo);
|
||||
} else {
|
||||
responseInfo = [QNResponseInfo errorResponseInfo:NSURLErrorCannotDecodeRawData errorDesc:[NSString stringWithFormat:@"origin response:%@", responseInfo]];
|
||||
ret(responseInfo, metrics, nil);
|
||||
}
|
||||
} else {
|
||||
if (self.defaultZone != nil) {
|
||||
// 备用只能用一次
|
||||
QNZonesInfo *info = [self.defaultZone getZonesInfoWithToken:token];
|
||||
[self setZonesInfo:info forKey:cacheKey];
|
||||
responseInfo = [QNResponseInfo successResponseWithDesc:[NSString stringWithFormat:@"origin response:%@", responseInfo]];
|
||||
ret(responseInfo, metrics, info);
|
||||
} else if (zonesInfo != nil) {
|
||||
// 缓存有,但是失效也可使用
|
||||
[self setZonesInfo:zonesInfo forKey:cacheKey];
|
||||
responseInfo = [QNResponseInfo successResponseWithDesc:[NSString stringWithFormat:@"origin response:%@", responseInfo]];
|
||||
ret(responseInfo, metrics, zonesInfo);
|
||||
} else {
|
||||
ret(responseInfo, metrics, nil);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (QNRequestTransaction *)createUploadRequestTransaction:(QNConfiguration *)config token:(QNUpToken *)token {
|
||||
if (config == nil) {
|
||||
config = [QNConfiguration defaultConfiguration];
|
||||
}
|
||||
|
||||
NSArray *hosts = nil;
|
||||
if (self.ucHosts && self.ucHosts.count > 0) {
|
||||
hosts = [self.ucHosts copy];
|
||||
} else {
|
||||
hosts = kQNPreQueryHosts;
|
||||
}
|
||||
QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithConfig:config
|
||||
uploadOption:[QNUploadOption defaultOptions]
|
||||
hosts:hosts
|
||||
regionId:QNZoneInfoEmptyRegionId
|
||||
key:@""
|
||||
token:token];
|
||||
@synchronized (self) {
|
||||
[self.transactions addObject:transaction];
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
- (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction {
|
||||
if (transaction) {
|
||||
@synchronized (self) {
|
||||
[self.transactions removeObject:transaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)makeCacheKey:(QNConfiguration *)config akAndBucket:(NSString *)akAndBucket {
|
||||
NSString *key = akAndBucket;
|
||||
if (config != nil) {
|
||||
key = [NSString stringWithFormat:@"%@:%d",key, config.accelerateUploading];
|
||||
}
|
||||
|
||||
NSString *hosts = @"";
|
||||
for (NSString *host in self.ucHosts) {
|
||||
if (!host) {
|
||||
continue;
|
||||
}
|
||||
hosts = [NSString stringWithFormat:@"%@:%@", hosts, host];
|
||||
}
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"%@:%@", hosts, key];
|
||||
return [QNUrlSafeBase64 encodeString:cacheKey];
|
||||
}
|
||||
|
||||
@end
|
||||
18
Pods/Qiniu/QiniuSDK/Common/QNConfig.h
generated
Normal file
18
Pods/Qiniu/QiniuSDK/Common/QNConfig.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// QNConfig.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//MARK: -- 内部布置 尽量不要修改
|
||||
#define kQNPreQueryHost00 @"uc.qiniuapi.com"
|
||||
#define kQNPreQueryHost01 @"kodo-config.qiniuapi.com"
|
||||
#define kQNPreQueryHost02 @"uc.qbox.me"
|
||||
#define kQNPreQueryHost03 @"api.qiniu.com"
|
||||
#define kQNPreQueryHosts @[kQNPreQueryHost00, kQNPreQueryHost01, kQNPreQueryHost02]
|
||||
|
||||
#define kQNUpLogHost @"uplog.qbox.me"
|
||||
68
Pods/Qiniu/QiniuSDK/Common/QNErrorCode.h
generated
Normal file
68
Pods/Qiniu/QiniuSDK/Common/QNErrorCode.h
generated
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// QNErrorCode.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/10/21.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
/**
|
||||
* StatusCode >= 100 见:https://developer.qiniu.com/kodo/3928/error-responses
|
||||
* 除上述链接及下面定义外的状态码依据 iOS 标准库定义
|
||||
*/
|
||||
|
||||
/**
|
||||
* 中途取消的状态码
|
||||
*/
|
||||
extern const int kQNRequestCancelled;
|
||||
|
||||
/**
|
||||
* 网络错误状态码
|
||||
*/
|
||||
extern const int kQNNetworkError;
|
||||
|
||||
/**
|
||||
* 错误参数状态码
|
||||
*/
|
||||
extern const int kQNInvalidArgument;
|
||||
|
||||
/**
|
||||
* 0 字节文件或数据
|
||||
*/
|
||||
extern const int kQNZeroDataSize;
|
||||
|
||||
/**
|
||||
* 错误token状态码
|
||||
*/
|
||||
extern const int kQNInvalidToken;
|
||||
|
||||
/**
|
||||
* 读取文件错误状态码
|
||||
*/
|
||||
extern const int kQNFileError;
|
||||
|
||||
/**
|
||||
* 本地 I/O 错误
|
||||
*/
|
||||
extern const int kQNLocalIOError;
|
||||
|
||||
/**
|
||||
* ⽤户劫持错误 错误
|
||||
*/
|
||||
extern const int kQNMaliciousResponseError;
|
||||
|
||||
/**
|
||||
* 没有可用的Host 错误【废弃】
|
||||
*/
|
||||
extern const int kQNNoUsableHostError NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* SDK 内部错误
|
||||
*/
|
||||
extern const int kQNSDKInteriorError;
|
||||
|
||||
/**
|
||||
* 非预期的系统调用 错误
|
||||
*/
|
||||
extern const int kQNUnexpectedSysCallError;
|
||||
25
Pods/Qiniu/QiniuSDK/Common/QNErrorCode.m
generated
Normal file
25
Pods/Qiniu/QiniuSDK/Common/QNErrorCode.m
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// QNErrorCode.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/10/21.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNErrorCode.h"
|
||||
|
||||
const int kQNUnexpectedSysCallError = -10;
|
||||
const int kQNNoUsableHostError = -9;
|
||||
const int kQNSDKInteriorError = -9;
|
||||
const int kQNMaliciousResponseError = -8;
|
||||
const int kQNLocalIOError = -7;
|
||||
const int kQNZeroDataSize = -6;
|
||||
const int kQNInvalidToken = -5;
|
||||
const int kQNFileError = -4;
|
||||
const int kQNInvalidArgument = -3;
|
||||
const int kQNRequestCancelled = -2;
|
||||
const int kQNNetworkError = -1;
|
||||
|
||||
|
||||
|
||||
|
||||
112
Pods/Qiniu/QiniuSDK/Common/QNFixedZone.h
generated
Normal file
112
Pods/Qiniu/QiniuSDK/Common/QNFixedZone.h
generated
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// QNFixZone.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNZone.h"
|
||||
#import "QNDefine.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNFixedZone : QNZone
|
||||
|
||||
/**
|
||||
* zone 0 华东
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zone0 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* zoneCnEast2 华东-浙江2
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zoneCnEast2 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* zone 1 华北
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zone1 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* zone 2 华南
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zone2 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* zone Na0 北美
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zoneNa0 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* zone As0 新加坡
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)zoneAs0 kQNDeprecated("use createWithRegionId instead");
|
||||
|
||||
/**
|
||||
* Zone初始化方法
|
||||
*
|
||||
* @param upList 默认上传服务器地址列表
|
||||
* @return Zone实例
|
||||
*/
|
||||
- (instancetype)initWithUpDomainList:(NSArray<NSString *> *)upList;
|
||||
|
||||
|
||||
/**
|
||||
* Zone初始化方法
|
||||
*
|
||||
* @param accUpList 加速上传服务器地址列表
|
||||
* @param upList 默认上传服务器地址列表
|
||||
* @param oldUpList 支持 SNI 的上传服务器地址列表
|
||||
* @param regionId 区域 ID
|
||||
* @return Zone实例
|
||||
*/
|
||||
- (instancetype)initWithAccUpDomainList:(NSArray<NSString *> *)accUpList
|
||||
upList:(NSArray<NSString *> *)upList
|
||||
oldUpList:(NSArray<NSString *> *)oldUpList
|
||||
regionId:(NSString *)regionId;
|
||||
|
||||
/**
|
||||
* Zone初始化方法
|
||||
*
|
||||
* @param upList 默认上传服务器地址列表
|
||||
*
|
||||
* @return Zone实例
|
||||
*/
|
||||
+ (instancetype)createWithHost:(NSArray<NSString *> *)upList;
|
||||
|
||||
/**
|
||||
* Zone初始化方法
|
||||
* regionId 参考链接:https://developer.qiniu.com/kodo/1671/region-endpoint-fq
|
||||
*
|
||||
* @param regionId 根据区域 ID 创建 Zone
|
||||
*
|
||||
* @return Zone 实例
|
||||
*/
|
||||
+ (instancetype)createWithRegionId:(NSString *)regionId;
|
||||
|
||||
/**
|
||||
* 获取本地所有固定zone信息
|
||||
*/
|
||||
+ (QNFixedZone *)localsZoneInfo DEPRECATED_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* 合并区域
|
||||
*/
|
||||
+ (QNFixedZone *)combineZones:(NSArray<QNFixedZone *> *)zones;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
199
Pods/Qiniu/QiniuSDK/Common/QNFixedZone.m
generated
Normal file
199
Pods/Qiniu/QiniuSDK/Common/QNFixedZone.m
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// QNFixZone.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNFixedZone.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNResponseInfo.h"
|
||||
|
||||
@interface QNFixedZone ()
|
||||
|
||||
@property (nonatomic, strong) QNZonesInfo *zonesInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNFixedZone
|
||||
|
||||
+ (instancetype)zone0 {
|
||||
static QNFixedZone *z0 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
z0 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload.qiniup.com", @"up.qiniup.com"]
|
||||
oldUpList:@[@"upload.qbox.me", @"up.qbox.me"]
|
||||
regionId:@"z0"];
|
||||
});
|
||||
return z0;
|
||||
}
|
||||
|
||||
+ (instancetype)zoneCnEast2 {
|
||||
static QNFixedZone *zoneCnEast2 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
zoneCnEast2 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload-cn-east-2.qiniup.com", @"up-cn-east-2.qiniup.com"]
|
||||
oldUpList:nil
|
||||
regionId:@"cn-east-2"];
|
||||
});
|
||||
return zoneCnEast2;
|
||||
}
|
||||
|
||||
+ (instancetype)zone1 {
|
||||
static QNFixedZone *z1 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
z1 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload-z1.qiniup.com", @"up-z1.qiniup.com"]
|
||||
oldUpList:@[@"upload-z1.qbox.me", @"up-z1.qbox.me"]
|
||||
regionId:@"z1"];
|
||||
});
|
||||
return z1;
|
||||
}
|
||||
|
||||
+ (instancetype)zone2 {
|
||||
static QNFixedZone *z2 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
z2 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload-z2.qiniup.com", @"up-z2.qiniup.com"]
|
||||
oldUpList:@[@"upload-z2.qbox.me", @"up-z2.qbox.me"]
|
||||
regionId:@"z2"];
|
||||
});
|
||||
return z2;
|
||||
}
|
||||
|
||||
+ (instancetype)zoneNa0 {
|
||||
static QNFixedZone *zNa0 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
zNa0 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload-na0.qiniup.com", @"up-na0.qiniup.com"]
|
||||
oldUpList:@[@"upload-na0.qbox.me", @"up-na0.qbox.me"]
|
||||
regionId:@"na0"];
|
||||
});
|
||||
return zNa0;
|
||||
}
|
||||
|
||||
+ (instancetype)zoneAs0 {
|
||||
static QNFixedZone *zAs0 = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
zAs0 = [[QNFixedZone alloc] initWithUpDomainList:@[@"upload-as0.qiniup.com", @"up-as0.qiniup.com"]
|
||||
oldUpList:@[@"upload-as0.qbox.me", @"up-as0.qbox.me"]
|
||||
regionId:@"as0"];;
|
||||
});
|
||||
return zAs0;
|
||||
}
|
||||
|
||||
+ (QNFixedZone *)localsZoneInfo{
|
||||
|
||||
NSArray *zones = @[[QNFixedZone zone0],
|
||||
[QNFixedZone zone1],
|
||||
[QNFixedZone zone2],
|
||||
[QNFixedZone zoneCnEast2],
|
||||
[QNFixedZone zoneNa0],
|
||||
[QNFixedZone zoneAs0]];
|
||||
QNFixedZone *zone = [self combineZones:zones];
|
||||
if (zone) {
|
||||
[zone.zonesInfo toTemporary];
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
+ (QNFixedZone *)combineZones:(NSArray<QNFixedZone *> *)zones {
|
||||
if (zones == nil || zones.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray <QNZoneInfo *> *zoneInfoArray = [NSMutableArray array];
|
||||
for (QNFixedZone *zone in zones) {
|
||||
if (zone.zonesInfo.zonesInfo) {
|
||||
[zoneInfoArray addObjectsFromArray:zone.zonesInfo.zonesInfo];
|
||||
}
|
||||
}
|
||||
|
||||
QNFixedZone *fixedZone = [[QNFixedZone alloc] init];
|
||||
fixedZone.zonesInfo = [[QNZonesInfo alloc] initWithZonesInfo:[zoneInfoArray copy]];
|
||||
[fixedZone.zonesInfo toTemporary];
|
||||
return fixedZone;
|
||||
}
|
||||
|
||||
+ (instancetype)createWithHost:(NSArray<NSString *> *)upList {
|
||||
return [[QNFixedZone alloc] initWithUpDomainList:upList oldUpList:nil regionId:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)createWithRegionId:(NSString *)regionId {
|
||||
NSArray *upList = @[
|
||||
[NSString stringWithFormat:@"upload-%@.qiniup.com", regionId],
|
||||
[NSString stringWithFormat:@"up-%@.qiniup.com", regionId],
|
||||
];
|
||||
return [[QNFixedZone alloc] initWithUpDomainList:upList oldUpList:nil regionId:regionId];
|
||||
}
|
||||
|
||||
- (QNZonesInfo *)createZonesInfo:(NSArray <NSString *> *)upDomains
|
||||
regionId:(NSString *)regionId {
|
||||
return [self createZonesInfo:upDomains oldUpDomains:nil regionId:regionId];
|
||||
}
|
||||
|
||||
- (QNZonesInfo *)createZonesInfo:(NSArray <NSString *> *)upDomains
|
||||
oldUpDomains:(NSArray <NSString *> *)oldUpDomains
|
||||
regionId:(NSString *)regionId {
|
||||
return [self createZonesInfo:nil domains:upDomains oldDomains:oldUpDomains regionId:regionId];
|
||||
}
|
||||
|
||||
- (QNZonesInfo *)createZonesInfo:(NSArray <NSString *> *)accDomains
|
||||
domains:(NSArray <NSString *> *)domains
|
||||
oldDomains:(NSArray <NSString *> *)oldDomains
|
||||
regionId:(NSString *)regionId {
|
||||
if ((!accDomains || accDomains.count == 0) && (!domains || domains.count == 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
QNZoneInfo *zoneInfo = [QNZoneInfo zoneInfoWithAccHosts:accDomains
|
||||
mainHosts:domains
|
||||
oldHosts:oldDomains
|
||||
regionId:regionId];
|
||||
QNZonesInfo *zonesInfo = [[QNZonesInfo alloc] initWithZonesInfo:@[zoneInfo]];
|
||||
return zonesInfo;
|
||||
}
|
||||
|
||||
- (instancetype)initWithUpDomainList:(NSArray<NSString *> *)upList {
|
||||
if (self = [super init]) {
|
||||
self.zonesInfo = [self createZonesInfo:upList regionId:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithUpDomainList:(NSArray<NSString *> *)upList
|
||||
regionId:(NSString *)regionId {
|
||||
if (self = [super init]) {
|
||||
self.zonesInfo = [self createZonesInfo:upList regionId:regionId];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithUpDomainList:(NSArray<NSString *> *)upList
|
||||
oldUpList:(NSArray<NSString *> *)oldUpList
|
||||
regionId:(NSString *)regionId {
|
||||
if (self = [super init]) {
|
||||
self.zonesInfo = [self createZonesInfo:upList oldUpDomains:oldUpList regionId:regionId];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithAccUpDomainList:(NSArray<NSString *> *)accUpList
|
||||
upList:(NSArray<NSString *> *)upList
|
||||
oldUpList:(NSArray<NSString *> *)oldUpList
|
||||
regionId:(NSString *)regionId {
|
||||
if (self = [super init]) {
|
||||
self.zonesInfo = [self createZonesInfo:accUpList domains:upList oldDomains:oldUpList regionId:regionId];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken *)token {
|
||||
return self.zonesInfo;
|
||||
}
|
||||
|
||||
- (void)query:(QNConfiguration * _Nullable)config token:(QNUpToken * _Nullable)token on:(QNQueryReturn _Nullable)ret {
|
||||
ret([QNResponseInfo successResponse], nil, self.zonesInfo);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
38
Pods/Qiniu/QiniuSDK/Common/QNZone.h
generated
Normal file
38
Pods/Qiniu/QiniuSDK/Common/QNZone.h
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// QNZone.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNConfiguration, QNResponseInfo, QNUpToken, QNZonesInfo, QNUploadRegionRequestMetrics;
|
||||
|
||||
typedef void (^QNPrequeryReturn)(int code, QNResponseInfo * _Nullable httpResponseInfo, QNUploadRegionRequestMetrics * _Nullable metrics);
|
||||
typedef void (^QNQueryReturn)(QNResponseInfo * _Nullable httpResponseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, QNZonesInfo * _Nullable zonesInfo);
|
||||
|
||||
@interface QNZone : NSObject
|
||||
|
||||
/// 根据token查询相关 Zone 信息【内部使用】
|
||||
/// @param token token 信息
|
||||
/// @param ret 查询回调
|
||||
- (void)preQuery:(QNUpToken * _Nullable)token on:(QNPrequeryReturn _Nullable)ret;
|
||||
|
||||
/// 根据token获取ZonesInfo 【内部使用】
|
||||
/// @param token token信息
|
||||
- (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken * _Nullable)token;
|
||||
|
||||
|
||||
/// 根据token查询相关 Zone 信息【内部使用】
|
||||
/// @param config 配置信息
|
||||
/// @param token token 信息
|
||||
/// @param ret 查询回调
|
||||
- (void)query:(QNConfiguration * _Nullable)config token:(QNUpToken * _Nullable)token on:(QNQueryReturn _Nullable)ret;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
30
Pods/Qiniu/QiniuSDK/Common/QNZone.m
generated
Normal file
30
Pods/Qiniu/QiniuSDK/Common/QNZone.m
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// QNZone.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNZone.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNResponseInfo.h"
|
||||
|
||||
@implementation QNZone
|
||||
|
||||
- (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken *)token {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)preQuery:(QNUpToken *)token
|
||||
on:(QNPrequeryReturn)ret {
|
||||
ret(0, nil, nil);
|
||||
}
|
||||
|
||||
- (void)query:(QNConfiguration * _Nullable)config token:(QNUpToken * _Nullable)token on:(QNQueryReturn _Nullable)ret {
|
||||
ret([QNResponseInfo responseInfoWithSDKInteriorError:@"impl query"], nil, nil);
|
||||
}
|
||||
|
||||
@end
|
||||
68
Pods/Qiniu/QiniuSDK/Common/QNZoneInfo.h
generated
Normal file
68
Pods/Qiniu/QiniuSDK/Common/QNZoneInfo.h
generated
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// QNZoneInfo.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNCache.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const QNZoneInfoSDKDefaultIOHost;
|
||||
extern NSString *const QNZoneInfoEmptyRegionId;
|
||||
|
||||
@interface QNZoneInfo : NSObject
|
||||
|
||||
@property(nonatomic, assign, readonly)long ttl;
|
||||
@property(nonatomic, assign, readonly)BOOL http3Enabled;
|
||||
@property(nonatomic, strong, readonly)NSArray<NSString *> *domains;
|
||||
@property(nonatomic, strong, readonly)NSArray<NSString *> *acc_domains;
|
||||
@property(nonatomic, strong, readonly)NSArray<NSString *> *old_domains;
|
||||
|
||||
@property(nonatomic, copy, readonly)NSString *regionId;
|
||||
@property(nonatomic, strong, readonly)NSArray <NSString *> *allHosts;
|
||||
@property(nonatomic, strong, readonly)NSDictionary *detailInfo;
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithMainHosts:(NSArray <NSString *> *)mainHosts
|
||||
regionId:(NSString * _Nullable)regionId;
|
||||
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithMainHosts:(NSArray <NSString *> *)mainHosts
|
||||
oldHosts:(NSArray <NSString *> * _Nullable)oldHosts
|
||||
regionId:(NSString * _Nullable)regionId;
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithAccHosts:(NSArray <NSString *> *)accHosts
|
||||
mainHosts:(NSArray <NSString *> *)mainHosts
|
||||
oldHosts:(NSArray <NSString *> * _Nullable)oldHosts
|
||||
regionId:(NSString * _Nullable)regionId;
|
||||
|
||||
/// 根据键值对构造对象 【内部使用】
|
||||
/// @param detailInfo 键值对信息
|
||||
+ (QNZoneInfo *)zoneInfoFromDictionary:(NSDictionary *)detailInfo;
|
||||
|
||||
- (BOOL)isValid;
|
||||
|
||||
@end
|
||||
|
||||
@interface QNZonesInfo : NSObject <QNCacheObject>
|
||||
|
||||
@property (nonatomic, strong, readonly) NSDate *buildDate;
|
||||
@property (nonatomic, assign, readonly) BOOL isTemporary;
|
||||
@property (nonatomic, assign, readonly) BOOL isValid;
|
||||
@property (nonatomic, strong, readonly) NSArray<QNZoneInfo *> *zonesInfo;
|
||||
|
||||
/// 根据键值对构造对象 【内部使用】
|
||||
/// @param dictionary 键值对信息
|
||||
+ (instancetype)infoWithDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
- (instancetype)initWithZonesInfo:(NSArray<QNZoneInfo *> *)zonesInfo;
|
||||
|
||||
// 转成临时 zones, 临时 Zones,不建议长期使用
|
||||
- (void)toTemporary;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
250
Pods/Qiniu/QiniuSDK/Common/QNZoneInfo.m
generated
Normal file
250
Pods/Qiniu/QiniuSDK/Common/QNZoneInfo.m
generated
Normal file
@@ -0,0 +1,250 @@
|
||||
//
|
||||
// QNZoneInfo.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/16.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUtils.h"
|
||||
|
||||
NSString * const QNZoneInfoSDKDefaultIOHost = @"default_io_host";
|
||||
NSString * const QNZoneInfoEmptyRegionId = @"none";
|
||||
|
||||
@interface QNZoneInfo()
|
||||
|
||||
@property(nonatomic, strong) NSDate *buildDate;
|
||||
|
||||
@property(nonatomic, copy) NSString *regionId;
|
||||
@property(nonatomic, assign) long ttl;
|
||||
@property(nonatomic, assign) BOOL http3Enabled;
|
||||
@property(nonatomic, strong) NSArray<NSString *> *domains;
|
||||
@property(nonatomic, strong) NSArray<NSString *> *acc_domains;
|
||||
@property(nonatomic, strong) NSArray<NSString *> *old_domains;
|
||||
|
||||
@property(nonatomic, strong) NSArray <NSString *> *allHosts;
|
||||
@property(nonatomic, strong) NSDictionary *detailInfo;
|
||||
|
||||
@end
|
||||
@implementation QNZoneInfo
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithMainHosts:(NSArray <NSString *> *)mainHosts
|
||||
regionId:(NSString * _Nullable)regionId{
|
||||
return [self zoneInfoWithMainHosts:mainHosts oldHosts:nil regionId:regionId];
|
||||
}
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithMainHosts:(NSArray <NSString *> *)mainHosts
|
||||
oldHosts:(NSArray <NSString *> * _Nullable)oldHosts
|
||||
regionId:(NSString * _Nullable)regionId{
|
||||
|
||||
if (!mainHosts || ![mainHosts isKindOfClass:[NSArray class]] || mainHosts.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (mainHosts && ![mainHosts isKindOfClass:[NSArray class]]) {
|
||||
mainHosts = nil;
|
||||
}
|
||||
|
||||
QNZoneInfo *zoneInfo = [QNZoneInfo zoneInfoFromDictionary:@{@"ttl" : @(-1),
|
||||
@"region" : regionId ?: QNZoneInfoEmptyRegionId,
|
||||
@"up" : @{@"domains" : mainHosts ?: @[],
|
||||
@"old" : oldHosts ?: @[]},
|
||||
}];
|
||||
return zoneInfo;
|
||||
}
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoWithAccHosts:(NSArray <NSString *> *)accHosts
|
||||
mainHosts:(NSArray <NSString *> *)mainHosts
|
||||
oldHosts:(NSArray <NSString *> * _Nullable)oldHosts
|
||||
regionId:(NSString * _Nullable)regionId {
|
||||
if ((!accHosts || ![accHosts isKindOfClass:[NSArray class]] || accHosts.count == 0) &&
|
||||
(!mainHosts || ![mainHosts isKindOfClass:[NSArray class]] || mainHosts.count == 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (accHosts && ![accHosts isKindOfClass:[NSArray class]]) {
|
||||
accHosts = nil;
|
||||
}
|
||||
|
||||
if (mainHosts && ![mainHosts isKindOfClass:[NSArray class]]) {
|
||||
mainHosts = nil;
|
||||
}
|
||||
|
||||
QNZoneInfo *zoneInfo = [QNZoneInfo zoneInfoFromDictionary:@{@"ttl" : @(-1),
|
||||
@"region" : regionId ?: QNZoneInfoEmptyRegionId,
|
||||
@"up" : @{@"acc_domains" : accHosts ?: @[],
|
||||
@"domains" : mainHosts ?: @[],
|
||||
@"old" : oldHosts ?: @[]},
|
||||
}];
|
||||
return zoneInfo;
|
||||
}
|
||||
|
||||
+ (QNZoneInfo *)zoneInfoFromDictionary:(NSDictionary *)detail {
|
||||
if (![detail isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary *detailInfo = [detail mutableCopy];
|
||||
if (detailInfo[@"timestamp"] == nil) {
|
||||
detailInfo[@"timestamp"] = @([QNUtils currentTimestamp]/1000);
|
||||
}
|
||||
long timestamp = [detailInfo[@"timestamp"] longValue];
|
||||
|
||||
NSString *regionId = [detailInfo objectForKey:@"region"];
|
||||
if (regionId == nil) {
|
||||
regionId = QNZoneInfoEmptyRegionId;
|
||||
}
|
||||
long ttl = [[detailInfo objectForKey:@"ttl"] longValue];
|
||||
BOOL http3Enabled = false;
|
||||
if ([detailInfo[@"features"] isKindOfClass:[NSDictionary class]] &&
|
||||
[detailInfo[@"features"][@"http3"] isKindOfClass:[NSDictionary class]]) {
|
||||
http3Enabled = [detailInfo[@"features"][@"http3"][@"enabled"] boolValue];
|
||||
}
|
||||
NSDictionary *up = [detailInfo objectForKey:@"up"];
|
||||
NSArray *acc_domains = [up objectForKey:@"acc_domains"];
|
||||
NSArray *domains = [up objectForKey:@"domains"];
|
||||
NSArray *old_domains = [up objectForKey:@"old"];
|
||||
|
||||
NSMutableArray *allHosts = [NSMutableArray array];
|
||||
QNZoneInfo *zoneInfo = [[QNZoneInfo alloc] init:ttl regionId:regionId];
|
||||
zoneInfo.buildDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
|
||||
zoneInfo.http3Enabled = http3Enabled;
|
||||
if ([acc_domains isKindOfClass:[NSArray class]]) {
|
||||
zoneInfo.acc_domains = acc_domains;
|
||||
[allHosts addObjectsFromArray:domains];
|
||||
}
|
||||
if ([domains isKindOfClass:[NSArray class]]) {
|
||||
zoneInfo.domains = domains;
|
||||
[allHosts addObjectsFromArray:domains];
|
||||
}
|
||||
if ([old_domains isKindOfClass:[NSArray class]]) {
|
||||
zoneInfo.old_domains = old_domains;
|
||||
[allHosts addObjectsFromArray:old_domains];
|
||||
}
|
||||
zoneInfo.allHosts = [allHosts copy];
|
||||
|
||||
zoneInfo.detailInfo = [detailInfo copy];
|
||||
|
||||
return zoneInfo;
|
||||
}
|
||||
|
||||
- (instancetype)init:(long)ttl
|
||||
regionId:(NSString *)regionId {
|
||||
if (self = [super init]) {
|
||||
_ttl = ttl;
|
||||
_buildDate = [NSDate date];
|
||||
_regionId = regionId;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isValid{
|
||||
if (self.allHosts == nil || self.allHosts.count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self.ttl < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
NSDate *currentDate = [NSDate date];
|
||||
return self.ttl > [currentDate timeIntervalSinceDate:self.buildDate];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
QNZoneInfo *zoneInfo = [[QNZoneInfo allocWithZone:zone] init];
|
||||
zoneInfo.ttl = self.ttl;
|
||||
zoneInfo.buildDate = self.buildDate;
|
||||
zoneInfo.http3Enabled = self.http3Enabled;
|
||||
zoneInfo.regionId = self.regionId;
|
||||
zoneInfo.domains = [self.domains copy];
|
||||
zoneInfo.old_domains = [self.old_domains copy];
|
||||
zoneInfo.allHosts = [self.allHosts copy];
|
||||
zoneInfo.detailInfo = [self.detailInfo copy];
|
||||
return zoneInfo;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@interface QNZonesInfo()
|
||||
@property (nonatomic, strong) NSDate *buildDate;
|
||||
@property (nonatomic, assign) BOOL isTemporary;
|
||||
@property (nonatomic, strong) NSArray<QNZoneInfo *> *zonesInfo;
|
||||
@property (nonatomic, strong) NSDictionary *detailInfo;
|
||||
@end
|
||||
@implementation QNZonesInfo
|
||||
|
||||
+ (instancetype)infoWithDictionary:(NSDictionary *)dictionary {
|
||||
return [[self alloc] initWithDictionary:dictionary];
|
||||
}
|
||||
|
||||
- (nonnull id<QNCacheObject>)initWithDictionary:(nullable NSDictionary *)dictionary {
|
||||
NSMutableArray *zonesInfo = [NSMutableArray array];
|
||||
NSArray *hosts = dictionary[@"hosts"];
|
||||
if ([hosts isKindOfClass:[NSArray class]]) {
|
||||
for (NSInteger i = 0; i < hosts.count; i++) {
|
||||
QNZoneInfo *zoneInfo = [QNZoneInfo zoneInfoFromDictionary:hosts[i]];
|
||||
if (zoneInfo && [zoneInfo allHosts].count > 0) {
|
||||
[zonesInfo addObject:zoneInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [self initWithZonesInfo:zonesInfo];
|
||||
}
|
||||
|
||||
- (instancetype)initWithZonesInfo:(NSArray<QNZoneInfo *> *)zonesInfo{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_buildDate = [NSDate date];
|
||||
_zonesInfo = zonesInfo;
|
||||
NSMutableArray *zoneInfos = [NSMutableArray array];
|
||||
if (zonesInfo != nil) {
|
||||
for (NSInteger i = 0; i < zonesInfo.count; i++) {
|
||||
if (zonesInfo[i].detailInfo != nil) {
|
||||
[zoneInfos addObject:zonesInfo[i].detailInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
self.detailInfo = @{@"hosts": [zoneInfos copy]};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)toTemporary {
|
||||
_isTemporary = true;
|
||||
}
|
||||
|
||||
- (BOOL)isValid {
|
||||
if ([self.zonesInfo count] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL valid = true;
|
||||
for (QNZoneInfo *info in self.zonesInfo) {
|
||||
if (![info isValid]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
NSMutableArray *zonesInfoArray = [NSMutableArray array];
|
||||
for (QNZoneInfo *info in self.zonesInfo) {
|
||||
[zonesInfoArray addObject:[info copy]];
|
||||
}
|
||||
QNZonesInfo *zonesInfo = [[QNZonesInfo allocWithZone:zone] init];
|
||||
zonesInfo.zonesInfo = [zonesInfoArray copy];
|
||||
zonesInfo.isTemporary = self.isTemporary;
|
||||
return zonesInfo;
|
||||
}
|
||||
|
||||
- (nullable NSDictionary *)toDictionary {
|
||||
return [self.detailInfo copy];
|
||||
}
|
||||
|
||||
@end
|
||||
22
Pods/Qiniu/QiniuSDK/Http/ConnectCheck/QNConnectChecker.h
generated
Normal file
22
Pods/Qiniu/QiniuSDK/Http/ConnectCheck/QNConnectChecker.h
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// QNConnectChecker.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2021/1/8.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNConnectChecker : NSObject
|
||||
|
||||
+ (QNUploadSingleRequestMetrics *)check;
|
||||
|
||||
+ (BOOL)isConnected:(QNUploadSingleRequestMetrics *)metrics;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
152
Pods/Qiniu/QiniuSDK/Http/ConnectCheck/QNConnectChecker.m
generated
Normal file
152
Pods/Qiniu/QiniuSDK/Http/ConnectCheck/QNConnectChecker.m
generated
Normal file
@@ -0,0 +1,152 @@
|
||||
//
|
||||
// QNConnectChecker.m
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2021/1/8.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNSingleFlight.h"
|
||||
#import "QNConnectChecker.h"
|
||||
#import "QNUploadSystemClient.h"
|
||||
|
||||
@interface QNConnectChecker()
|
||||
|
||||
@end
|
||||
@implementation QNConnectChecker
|
||||
|
||||
+ (QNSingleFlight *)singleFlight {
|
||||
static QNSingleFlight *singleFlight = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
singleFlight = [[QNSingleFlight alloc] init];
|
||||
});
|
||||
return singleFlight;
|
||||
}
|
||||
|
||||
+ (dispatch_queue_t)checkQueue {
|
||||
static dispatch_queue_t queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = dispatch_queue_create("com.qiniu.NetworkCheckQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
+ (BOOL)isConnected:(QNUploadSingleRequestMetrics *)metrics {
|
||||
return metrics && ((NSHTTPURLResponse *)metrics.response).statusCode > 99;
|
||||
}
|
||||
|
||||
+ (QNUploadSingleRequestMetrics *)check {
|
||||
__block QNUploadSingleRequestMetrics *metrics = nil;
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
[self check:^(QNUploadSingleRequestMetrics *metricsP) {
|
||||
metrics = metricsP;
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
+ (void)check:(void (^)(QNUploadSingleRequestMetrics *))complete {
|
||||
QNSingleFlight *singleFlight = [self singleFlight];
|
||||
|
||||
kQNWeakSelf;
|
||||
[singleFlight perform:@"connect_check" action:^(QNSingleFlightComplete _Nonnull singleFlightComplete) {
|
||||
kQNStrongSelf;
|
||||
|
||||
[self checkAllHosts:^(QNUploadSingleRequestMetrics *metrics) {
|
||||
singleFlightComplete(metrics, nil);
|
||||
}];
|
||||
|
||||
} complete:^(id _Nullable value, NSError * _Nullable error) {
|
||||
if (complete) {
|
||||
complete(value);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
+ (void)checkAllHosts:(void (^)(QNUploadSingleRequestMetrics *metrics))complete {
|
||||
|
||||
__block int completeCount = 0;
|
||||
__block BOOL isCompleted = false;
|
||||
kQNWeakSelf;
|
||||
NSArray *allHosts = [kQNGlobalConfiguration.connectCheckURLStrings copy];
|
||||
if (allHosts.count == 0) {
|
||||
QNUploadSingleRequestMetrics *metrics = [QNUploadSingleRequestMetrics emptyMetrics];
|
||||
[metrics start];
|
||||
[metrics end];
|
||||
metrics.error = [NSError errorWithDomain:@"com.qiniu.NetworkCheck" code:NSURLErrorUnsupportedURL userInfo:@{@"user_info":@"check host is empty"}];
|
||||
complete(metrics);
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSString *host in allHosts) {
|
||||
[self checkHost:host complete:^(QNUploadSingleRequestMetrics *metrics) {
|
||||
kQNStrongSelf;
|
||||
|
||||
BOOL isHostConnected = [self isConnected:metrics];
|
||||
@synchronized (self) {
|
||||
completeCount += 1;
|
||||
}
|
||||
if (isHostConnected || completeCount == allHosts.count) {
|
||||
@synchronized (self) {
|
||||
if (isCompleted) {
|
||||
QNLogInfo(@"== check all hosts has completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
|
||||
return;
|
||||
} else {
|
||||
QNLogInfo(@"== check all hosts completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
|
||||
isCompleted = true;
|
||||
}
|
||||
}
|
||||
complete(metrics);
|
||||
} else {
|
||||
QNLogInfo(@"== check all hosts not completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)checkHost:(NSString *)host complete:(void (^)(QNUploadSingleRequestMetrics *metrics))complete {
|
||||
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
request.URL = [NSURL URLWithString:host];
|
||||
request.HTTPMethod = @"HEAD";
|
||||
request.timeoutInterval = kQNGlobalConfiguration.connectCheckTimeout;
|
||||
|
||||
__block BOOL hasCallback = false;
|
||||
|
||||
QNUploadSingleRequestMetrics *timeoutMetric = [QNUploadSingleRequestMetrics emptyMetrics];
|
||||
[timeoutMetric start];
|
||||
|
||||
QNUploadSystemClient *client = [[QNUploadSystemClient alloc] init];
|
||||
[client request:request server:nil connectionProxy:nil progress:nil complete:^(NSURLResponse *response, QNUploadSingleRequestMetrics * metrics, NSData * _Nullable data, NSError * error) {
|
||||
@synchronized (self) {
|
||||
if (hasCallback) {
|
||||
return;
|
||||
}
|
||||
hasCallback = true;
|
||||
}
|
||||
QNLogInfo(@"== checkHost:%@ responseInfo:%@", host, response);
|
||||
complete(metrics);
|
||||
}];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * kQNGlobalConfiguration.connectCheckTimeout), [self checkQueue], ^{
|
||||
@synchronized (self) {
|
||||
if (hasCallback) {
|
||||
return;
|
||||
}
|
||||
hasCallback = true;
|
||||
}
|
||||
[client cancel];
|
||||
[timeoutMetric end];
|
||||
timeoutMetric.error = [NSError errorWithDomain:@"com.qiniu.NetworkCheck" code:NSURLErrorTimedOut userInfo:nil];
|
||||
complete(timeoutMetric);
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
41
Pods/Qiniu/QiniuSDK/Http/Dns/QNDns.h
generated
Normal file
41
Pods/Qiniu/QiniuSDK/Http/Dns/QNDns.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// QNDns.h
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol QNIDnsNetworkAddress <NSObject>
|
||||
|
||||
/// 域名
|
||||
@property(nonatomic, copy, readonly)NSString *hostValue;
|
||||
|
||||
/// 地址IP信息
|
||||
@property(nonatomic, copy, readonly)NSString *ipValue;
|
||||
|
||||
/// ip有效时间 单位:秒
|
||||
@property(nonatomic, strong, readonly)NSNumber *ttlValue;
|
||||
|
||||
/// ip预取来源, 自定义dns返回 @"customized"
|
||||
@property(nonatomic, copy, readonly)NSString *sourceValue;
|
||||
|
||||
/// 解析到host时的时间戳 单位:秒
|
||||
@property(nonatomic, strong, readonly)NSNumber *timestampValue;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@protocol QNDnsDelegate <NSObject>
|
||||
|
||||
/// 根据host获取解析结果
|
||||
/// @param host 域名
|
||||
- (NSArray < id <QNIDnsNetworkAddress> > *)query:(NSString *)host;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
29
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheFile.h
generated
Normal file
29
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheFile.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// QNDnsCacheFile.h
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNRecorderDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNDnsCacheFile : NSObject<QNRecorderDelegate>
|
||||
|
||||
/// DNS解析信息本地缓存路径
|
||||
@property(nonatomic, copy, readonly)NSString *directory;
|
||||
|
||||
/// 构造方法 路径不存在,或进行创建,创建失败返回为nil
|
||||
/// @param directory 路径
|
||||
/// @param error 构造错误时,会有值
|
||||
+ (instancetype _Nullable)dnsCacheFile:(NSString *)directory
|
||||
error:(NSError **)error;
|
||||
|
||||
- (void)clearCache:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
91
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheFile.m
generated
Normal file
91
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheFile.m
generated
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// QNDnsCacheFile.m
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDnsCacheFile.h"
|
||||
|
||||
@interface QNDnsCacheFile()
|
||||
|
||||
@property(nonatomic, copy)NSString *directory;
|
||||
|
||||
@end
|
||||
@implementation QNDnsCacheFile
|
||||
|
||||
+ (instancetype)dnsCacheFile:(NSString *)directory
|
||||
error:(NSError **)error{
|
||||
NSError *err = nil;
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&err];
|
||||
if (err != nil) {
|
||||
if (error != nil) *error = err;
|
||||
return nil;
|
||||
}
|
||||
|
||||
QNDnsCacheFile *f = [[QNDnsCacheFile alloc] init];
|
||||
f.directory = directory;
|
||||
return f;
|
||||
}
|
||||
|
||||
- (NSError *)set:(NSString *)key data:(NSData *)value {
|
||||
@synchronized (self) {
|
||||
NSError *error;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *filePath = [self pathOfKey:key];
|
||||
if ([fileManager fileExistsAtPath:filePath]) {
|
||||
[fileManager removeItemAtPath:filePath error:&error];
|
||||
}
|
||||
[fileManager createFileAtPath:filePath contents:value attributes:nil];
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)get:(NSString *)key {
|
||||
return [NSData dataWithContentsOfFile:[self pathOfKey:key]];
|
||||
}
|
||||
|
||||
- (NSError *)del:(NSString *)key {
|
||||
@synchronized (self) {
|
||||
NSError *error = nil;
|
||||
NSString *path = [self pathOfKey:key];
|
||||
if (path) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
[fileManager removeItemAtPath:path error:&error];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearCache:(NSError *__autoreleasing _Nullable *)error {
|
||||
@synchronized (self) {
|
||||
NSError *err;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
[fileManager removeItemAtPath:self.directory error:&err];
|
||||
if (err != nil) {
|
||||
if (error != nil) *error = err;
|
||||
return;
|
||||
}
|
||||
|
||||
[fileManager createDirectoryAtPath:self.directory withIntermediateDirectories:YES attributes:nil error:&err];
|
||||
if (error != nil) {
|
||||
*error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)getFileName{
|
||||
return @"dnsCache";
|
||||
}
|
||||
|
||||
- (NSString *)pathOfKey:(NSString *)key {
|
||||
return [QNDnsCacheFile pathJoin:key path:_directory];
|
||||
}
|
||||
|
||||
+ (NSString *)pathJoin:(NSString *)key
|
||||
path:(NSString *)path {
|
||||
return [[NSString alloc] initWithFormat:@"%@/%@", path, key];
|
||||
}
|
||||
|
||||
@end
|
||||
41
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheInfo.h
generated
Normal file
41
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheInfo.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// QNDnsCacheKey.h
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNDnsCacheInfo : NSObject
|
||||
|
||||
/// 缓存时间戳
|
||||
@property(nonatomic, copy, readonly)NSString *currentTime;
|
||||
/// 缓存时本地IP
|
||||
@property(nonatomic, copy, readonly)NSString *localIp;
|
||||
/// 缓存信息
|
||||
@property(nonatomic, copy, readonly)NSDictionary *info;
|
||||
|
||||
//MARK: -- 构造方法
|
||||
/// 根据json构造对象
|
||||
/// @param jsonData json数据
|
||||
+ (instancetype)dnsCacheInfo:(NSData *)jsonData;
|
||||
|
||||
/// 根据属性构造对象
|
||||
/// @param currentTime 缓存时间戳
|
||||
/// @param localIp 缓存时本地IP
|
||||
/// @param info 缓存信息
|
||||
+ (instancetype)dnsCacheInfo:(NSString *)currentTime
|
||||
localIp:(NSString *)localIp
|
||||
info:(NSDictionary *)info;
|
||||
|
||||
|
||||
/// 转化Json数据
|
||||
- (NSData *)jsonData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
58
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheInfo.m
generated
Normal file
58
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsCacheInfo.m
generated
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// QNDnsCacheKey.m
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDnsCacheInfo.h"
|
||||
|
||||
@interface QNDnsCacheInfo()
|
||||
/// 缓存时间戳
|
||||
@property(nonatomic, copy)NSString *currentTime;
|
||||
/// 缓存时本地IP
|
||||
@property(nonatomic, copy)NSString *localIp;
|
||||
/// 缓存信息
|
||||
@property(nonatomic, copy)NSDictionary *info;
|
||||
@end
|
||||
@implementation QNDnsCacheInfo
|
||||
|
||||
+ (instancetype)dnsCacheInfo:(NSData *)jsonData{
|
||||
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
|
||||
if (!info || info.count == 0 ||
|
||||
(!info[@"currentTime"] && !info[@"localIp"] && !info[@"info"])) {
|
||||
return nil;
|
||||
}
|
||||
return [QNDnsCacheInfo dnsCacheInfo:info[@"currentTime"]
|
||||
localIp:info[@"localIp"]
|
||||
info:info[@"info"]];;
|
||||
}
|
||||
|
||||
+ (instancetype)dnsCacheInfo:(NSString *)currentTime
|
||||
localIp:(NSString *)localIp
|
||||
info:(NSDictionary *)info{
|
||||
|
||||
QNDnsCacheInfo *cacheInfo = [[QNDnsCacheInfo alloc] init];
|
||||
cacheInfo.currentTime = currentTime;
|
||||
cacheInfo.localIp = localIp;
|
||||
cacheInfo.info = info;
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
- (NSData *)jsonData{
|
||||
NSMutableDictionary *cacheInfo = [NSMutableDictionary dictionary];
|
||||
if (self.currentTime) {
|
||||
cacheInfo[@"currentTime"] = self.currentTime;
|
||||
}
|
||||
if (self.localIp) {
|
||||
cacheInfo[@"localIp"] = self.localIp;
|
||||
}
|
||||
if (self.info) {
|
||||
cacheInfo[@"info"] = self.info;
|
||||
}
|
||||
return [NSJSONSerialization dataWithJSONObject:cacheInfo options:NSJSONWritingPrettyPrinted error:nil];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
66
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsPrefetch.h
generated
Normal file
66
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsPrefetch.h
generated
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// QNDnsPrefetch.h
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDns.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNTransactionManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define kQNDnsPrefetch [QNDnsPrefetch shared]
|
||||
@interface QNDnsPrefetch : NSObject
|
||||
|
||||
/// 最近一次预取错误信息
|
||||
@property(nonatomic, copy, readonly)NSString *lastPrefetchedErrorMessage;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
/// 根据host从缓存中读取DNS信息
|
||||
/// @param host 域名
|
||||
- (NSArray <id <QNIDnsNetworkAddress> > *)getInetAddressByHost:(NSString *)host;
|
||||
|
||||
/// 通过安全的方式预取 dns
|
||||
- (NSString *)prefetchHostBySafeDns:(NSString *)host error:(NSError **)error;
|
||||
|
||||
- (void)clearDnsCache:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface QNTransactionManager(Dns)
|
||||
|
||||
/// 添加加载本地dns事务
|
||||
- (void)addDnsLocalLoadTransaction;
|
||||
|
||||
/// 添加检测并预取dns事务 如果未开启DNS 或 事务队列中存在token对应的事务未处理,则返回NO
|
||||
/// @param currentZone 当前区域
|
||||
/// @param token token信息
|
||||
- (BOOL)addDnsCheckAndPrefetchTransaction:(QNZone *)currentZone token:(QNUpToken *)token;
|
||||
- (BOOL)addDnsCheckAndPrefetchTransaction:(QNConfiguration *)config zone:(QNZone *)currentZone token:(QNUpToken *)token;
|
||||
|
||||
/// 设置定时事务:检测已缓存DNS有效情况事务 无效会重新预取
|
||||
- (void)setDnsCheckWhetherCachedValidTransactionAction;
|
||||
|
||||
@end
|
||||
|
||||
#define kQNDnsSourceDoh @"doh"
|
||||
#define kQNDnsSourceUdp @"dns"
|
||||
#define kQNDnsSourceDnspod @"dnspod"
|
||||
#define kQNDnsSourceSystem @"system"
|
||||
#define kQNDnsSourceCustom @"customized"
|
||||
#define kQNDnsSourceNone @"none"
|
||||
|
||||
BOOL kQNIsDnsSourceDoh(NSString * _Nullable source);
|
||||
BOOL kQNIsDnsSourceUdp(NSString * _Nullable source);
|
||||
BOOL kQNIsDnsSourceDnsPod(NSString * _Nullable source);
|
||||
BOOL kQNIsDnsSourceSystem(NSString * _Nullable source);
|
||||
BOOL kQNIsDnsSourceCustom(NSString * _Nullable source);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
874
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsPrefetch.m
generated
Normal file
874
Pods/Qiniu/QiniuSDK/Http/Dns/QNDnsPrefetch.m
generated
Normal file
@@ -0,0 +1,874 @@
|
||||
//
|
||||
// QNDnsPrefetch.m
|
||||
// QnDNS
|
||||
//
|
||||
// Created by yangsen on 2020/3/26.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import "QNDnsPrefetch.h"
|
||||
#import "QNInetAddress.h"
|
||||
#import "QNDnsCacheInfo.h"
|
||||
#import "QNZoneInfo.h"
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNConfig.h"
|
||||
#import "QNDnsCacheFile.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNFixedZone.h"
|
||||
#import "QNAutoZone.h"
|
||||
#import <HappyDNS/HappyDNS.h>
|
||||
|
||||
|
||||
//MARK: -- 缓存模型
|
||||
@interface QNDnsNetworkAddress : NSObject<QNIDnsNetworkAddress>
|
||||
|
||||
@property(nonatomic, copy)NSString *hostValue;
|
||||
@property(nonatomic, copy)NSString *ipValue;
|
||||
@property(nonatomic, strong)NSNumber *ttlValue;
|
||||
@property(nonatomic, copy)NSString *sourceValue;
|
||||
@property(nonatomic, strong)NSNumber *timestampValue;
|
||||
|
||||
/// 构造方法 addressData为json String / Dictionary / Data / 遵循 QNIDnsNetworkAddress的实例
|
||||
+ (instancetype)inetAddress:(id)addressInfo;
|
||||
|
||||
/// 是否有效,根据时间戳判断
|
||||
- (BOOL)isValid;
|
||||
|
||||
/// 对象转json
|
||||
- (NSString *)toJsonInfo;
|
||||
|
||||
/// 对象转字典
|
||||
- (NSDictionary *)toDictionary;
|
||||
|
||||
@end
|
||||
@implementation QNDnsNetworkAddress
|
||||
|
||||
+ (instancetype)inetAddress:(id)addressInfo{
|
||||
|
||||
NSDictionary *addressDic = nil;
|
||||
if ([addressInfo isKindOfClass:[NSDictionary class]]) {
|
||||
addressDic = (NSDictionary *)addressInfo;
|
||||
} else if ([addressInfo isKindOfClass:[NSString class]]){
|
||||
NSData *data = [(NSString *)addressInfo dataUsingEncoding:NSUTF8StringEncoding];
|
||||
addressDic = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:NSJSONReadingMutableLeaves
|
||||
error:nil];
|
||||
} else if ([addressInfo isKindOfClass:[NSData class]]) {
|
||||
addressDic = [NSJSONSerialization JSONObjectWithData:(NSData *)addressInfo
|
||||
options:NSJSONReadingMutableLeaves
|
||||
error:nil];
|
||||
} else if ([addressInfo conformsToProtocol:@protocol(QNIDnsNetworkAddress)]){
|
||||
id <QNIDnsNetworkAddress> address = (id <QNIDnsNetworkAddress> )addressInfo;
|
||||
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
||||
if ([address respondsToSelector:@selector(hostValue)] && [address hostValue]) {
|
||||
dic[@"hostValue"] = [address hostValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(ipValue)] && [address ipValue]) {
|
||||
dic[@"ipValue"] = [address ipValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(ttlValue)] && [address ttlValue]) {
|
||||
dic[@"ttlValue"] = [address ttlValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(sourceValue)] && [address sourceValue]) {
|
||||
dic[@"sourceValue"] = [address sourceValue];
|
||||
} else {
|
||||
dic[@"sourceValue"] = kQNDnsSourceCustom;
|
||||
}
|
||||
if ([address respondsToSelector:@selector(timestampValue)] && [address timestampValue]) {
|
||||
dic[@"timestampValue"] = [address timestampValue];
|
||||
}
|
||||
addressDic = [dic copy];
|
||||
}
|
||||
|
||||
if (addressDic) {
|
||||
QNDnsNetworkAddress *address = [[QNDnsNetworkAddress alloc] init];
|
||||
[address setValuesForKeysWithDictionary:addressDic];
|
||||
return address;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/// 过了 ttl 时间则需要刷新
|
||||
- (BOOL)needRefresh{
|
||||
if (!self.timestampValue || !self.ipValue || self.ipValue.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
|
||||
return currentTimestamp > (self.timestampValue.doubleValue + self.ttlValue.doubleValue);
|
||||
}
|
||||
|
||||
/// 只要在最大 ttl 时间内,即为有效
|
||||
- (BOOL)isValid{
|
||||
if (!self.timestampValue || !self.ipValue || self.ipValue.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
|
||||
return currentTimestamp < (self.timestampValue.doubleValue + kQNGlobalConfiguration.dnsCacheMaxTTL);
|
||||
}
|
||||
|
||||
- (NSString *)toJsonInfo{
|
||||
NSString *defaultString = @"{}";
|
||||
NSDictionary *infoDic = [self toDictionary];
|
||||
if (!infoDic) {
|
||||
return defaultString;
|
||||
}
|
||||
|
||||
NSData *infoData = [NSJSONSerialization dataWithJSONObject:infoDic
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
if (!infoData) {
|
||||
return defaultString;
|
||||
}
|
||||
|
||||
NSString *infoStr = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding];
|
||||
if (!infoStr) {
|
||||
return defaultString;
|
||||
} else {
|
||||
return infoStr;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)toDictionary{
|
||||
return [self dictionaryWithValuesForKeys:@[@"ipValue", @"hostValue", @"ttlValue", @"sourceValue", @"timestampValue"]];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//MARK: -- HappyDNS 适配
|
||||
@interface QNRecord(DNS)<QNIDnsNetworkAddress>
|
||||
@end
|
||||
@implementation QNRecord(DNS)
|
||||
- (NSString *)hostValue{
|
||||
return nil;
|
||||
}
|
||||
- (NSString *)ipValue{
|
||||
return self.value;
|
||||
}
|
||||
- (NSNumber *)ttlValue{
|
||||
return @(self.ttl);
|
||||
}
|
||||
- (NSNumber *)timestampValue{
|
||||
return @(self.timeStamp);
|
||||
}
|
||||
- (NSString *)sourceValue{
|
||||
if (self.source == QNRecordSourceSystem) {
|
||||
return kQNDnsSourceSystem;
|
||||
} else if (self.source == QNRecordSourceDoh) {
|
||||
return [NSString stringWithFormat:@"%@<%@>", kQNDnsSourceDoh, self.server];
|
||||
} else if (self.source == QNRecordSourceUdp) {
|
||||
return [NSString stringWithFormat:@"%@<%@>", kQNDnsSourceUdp, self.server];
|
||||
} else if (self.source == QNRecordSourceDnspodEnterprise) {
|
||||
return kQNDnsSourceDnspod;
|
||||
} else if (self.ipValue == nil || self.ipValue.length == 0) {
|
||||
return kQNDnsSourceNone;
|
||||
} else {
|
||||
return kQNDnsSourceCustom;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface QNInternalDns : NSObject
|
||||
@property(nonatomic, strong)id<QNDnsDelegate> dns;
|
||||
@property(nonatomic, strong)id<QNResolverDelegate> resolver;
|
||||
@end
|
||||
@implementation QNInternalDns
|
||||
+ (instancetype)dnsWithDns:(id<QNDnsDelegate>)dns {
|
||||
QNInternalDns *interDns = [[QNInternalDns alloc] init];
|
||||
interDns.dns = dns;
|
||||
return interDns;
|
||||
}
|
||||
+ (instancetype)dnsWithResolver:(id<QNResolverDelegate>)resolver {
|
||||
QNInternalDns *interDns = [[QNInternalDns alloc] init];
|
||||
interDns.resolver = resolver;
|
||||
return interDns;
|
||||
}
|
||||
- (NSArray < id <QNIDnsNetworkAddress> > *)query:(NSString *)host error:(NSError **)error {
|
||||
if (self.dns && [self.dns respondsToSelector:@selector(query:)]) {
|
||||
return [self.dns query:host];
|
||||
} else if (self.resolver) {
|
||||
NSArray <QNRecord *>* records = [self.resolver query:[[QNDomain alloc] init:host] networkInfo:nil error:error];
|
||||
return [self filterRecords:records];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
- (NSArray <QNRecord *>*)filterRecords:(NSArray <QNRecord *>*)records {
|
||||
NSMutableArray <QNRecord *> *newRecords = [NSMutableArray array];
|
||||
for (QNRecord *record in records) {
|
||||
if (record.type == kQNTypeA || record.type == kQNTypeAAAA) {
|
||||
[newRecords addObject:record];
|
||||
}
|
||||
}
|
||||
return [newRecords copy];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
//MARK: -- DNS Prefetcher
|
||||
@interface QNDnsPrefetch()
|
||||
|
||||
// dns 预解析超时,默认3s
|
||||
@property(nonatomic, assign)int dnsPrefetchTimeout;
|
||||
|
||||
// 最近一次预取错误信息
|
||||
@property(nonatomic, copy)NSString *lastPrefetchedErrorMessage;
|
||||
/// 是否正在预取,正在预取会直接取消新的预取操作请求
|
||||
@property(atomic, assign)BOOL isPrefetching;
|
||||
/// 获取AutoZone时的同步锁
|
||||
@property(nonatomic, strong)dispatch_semaphore_t getAutoZoneSemaphore;
|
||||
/// DNS信息本地缓存key
|
||||
@property(nonatomic, strong)QNDnsCacheInfo *dnsCacheInfo;
|
||||
// 用户定制 dns
|
||||
@property(nonatomic, strong)QNInternalDns *customDns;
|
||||
// 系统 dns
|
||||
@property(nonatomic, strong)QNInternalDns *systemDns;
|
||||
/// prefetch hosts
|
||||
@property(nonatomic, strong)NSMutableSet *prefetchHosts;
|
||||
/// 缓存DNS解析结果
|
||||
/// 线程安全:内部方法均是在同一线程执行,读写不必加锁,对外开放接口读操作 需要和内部写操作枷锁
|
||||
@property(nonatomic, strong)NSMutableDictionary <NSString *, NSArray<QNDnsNetworkAddress *>*> *addressDictionary;
|
||||
@property(nonatomic, strong)QNDnsCacheFile *diskCache;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNDnsPrefetch
|
||||
|
||||
+ (instancetype)shared{
|
||||
static QNDnsPrefetch *prefetcher = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
prefetcher = [[QNDnsPrefetch alloc] init];
|
||||
});
|
||||
return prefetcher;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_isPrefetching = NO;
|
||||
_dnsPrefetchTimeout = 3;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//MARK: -- uploadManager初始化时,加载本地缓存到内存
|
||||
/// 同步本地预取缓存 如果存在且满足使用返回false,反之为true
|
||||
- (BOOL)recoverCache{
|
||||
id <QNRecorderDelegate> recorder = nil;
|
||||
|
||||
NSError *error;
|
||||
recorder = [QNDnsCacheFile dnsCacheFile:kQNGlobalConfiguration.dnsCacheDir
|
||||
error:&error];
|
||||
if (error) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSData *data = [recorder get:[QNIP local]];
|
||||
if (!data) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
QNDnsCacheInfo *cacheInfo = [QNDnsCacheInfo dnsCacheInfo:data];
|
||||
if (!cacheInfo) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSString *localIp = [QNIP local];
|
||||
|
||||
if (!localIp || localIp.length == 0 || ![cacheInfo.localIp isEqualToString:localIp]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self setDnsCacheInfo:cacheInfo];
|
||||
|
||||
return [self recoverDnsCache:cacheInfo.info];
|
||||
}
|
||||
/// 本地缓存读取失败后,加载本地域名,预取DNS解析信息
|
||||
- (void)localFetch{
|
||||
if ([self prepareToPreFetch] == NO) {
|
||||
return;
|
||||
}
|
||||
NSArray *hosts = [self getLocalPreHost];
|
||||
@synchronized (self) {
|
||||
[self.prefetchHosts addObjectsFromArray:hosts];
|
||||
}
|
||||
[self preFetchHosts:hosts];
|
||||
[self recorderDnsCache];
|
||||
[self endPreFetch];
|
||||
}
|
||||
//MARK: -- 检测并预取
|
||||
/// 根据token检测Dns缓存信息时效,无效则预取。 完成预取操作返回YES,反之返回NO
|
||||
- (void)checkAndPrefetchDnsIfNeed:(QNConfiguration *)config zone:(QNZone *)currentZone token:(QNUpToken *)token{
|
||||
if ([self prepareToPreFetch] == NO) {
|
||||
return;
|
||||
}
|
||||
NSArray *hosts = [self getCurrentZoneHosts:config zone:currentZone token:token];
|
||||
if (hosts == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
[self.prefetchHosts addObjectsFromArray:hosts];
|
||||
}
|
||||
[self preFetchHosts:hosts];
|
||||
[self recorderDnsCache];
|
||||
[self endPreFetch];
|
||||
}
|
||||
/// 检测已预取dns是否还有效,无效则重新预取
|
||||
- (void)checkWhetherCachedDnsValid{
|
||||
if ([self prepareToPreFetch] == NO) {
|
||||
return;
|
||||
}
|
||||
NSArray *hosts = nil;
|
||||
@synchronized (self) {
|
||||
hosts = [self.prefetchHosts allObjects];
|
||||
}
|
||||
[self preFetchHosts:hosts];
|
||||
[self recorderDnsCache];
|
||||
[self endPreFetch];
|
||||
}
|
||||
|
||||
//MARK: -- 读取缓存的DNS信息
|
||||
/// 根据host从缓存中读取DNS信息
|
||||
- (NSArray <id <QNIDnsNetworkAddress> > *)getInetAddressByHost:(NSString *)host{
|
||||
|
||||
if ([self isDnsOpen] == NO) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self clearDnsCacheIfNeeded];
|
||||
|
||||
NSArray <QNDnsNetworkAddress *> *addressList = nil;
|
||||
@synchronized (self) {
|
||||
addressList = self.addressDictionary[host];
|
||||
}
|
||||
|
||||
if (addressList && addressList.count > 0 && [addressList.firstObject isValid]) {
|
||||
return addressList;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidNetworkAddressOfHost:(NSString *)host {
|
||||
if (host == nil || host.length == 0) {
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
[self.addressDictionary removeObjectForKey:host];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearDnsCache:(NSError *__autoreleasing _Nullable *)error {
|
||||
[self clearDnsMemoryCache];
|
||||
[self clearDnsDiskCache:error];
|
||||
}
|
||||
|
||||
//MARK: --
|
||||
//MARK: -- 根据dns预取
|
||||
- (NSString *)prefetchHostBySafeDns:(NSString *)host error:(NSError * __autoreleasing *)error {
|
||||
if (host == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self invalidNetworkAddressOfHost:host];
|
||||
|
||||
NSError *err = nil;
|
||||
NSArray *nextFetchHosts = @[host];
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:self.customDns error:&err];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return [self getInetAddressByHost:host].firstObject.sourceValue;
|
||||
}
|
||||
|
||||
if (!kQNGlobalConfiguration.dohEnable) {
|
||||
if (error != nil && err) {
|
||||
*error = err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (kQNGlobalConfiguration.dohIpv4Servers && [kQNGlobalConfiguration.dohIpv4Servers count] > 0) {
|
||||
QNDohResolver *dohResolver = [QNDohResolver resolverWithServers:kQNGlobalConfiguration.dohIpv4Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *doh = [QNInternalDns dnsWithResolver:dohResolver];
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:doh error:&err];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return [self getInetAddressByHost:host].firstObject.sourceValue;
|
||||
}
|
||||
if (error != nil && err) {
|
||||
*error = err;
|
||||
}
|
||||
}
|
||||
|
||||
if ([QNIP isIpV6FullySupported] && kQNGlobalConfiguration.dohIpv6Servers && [kQNGlobalConfiguration.dohIpv6Servers count] > 0) {
|
||||
QNDohResolver *dohResolver = [QNDohResolver resolverWithServers:kQNGlobalConfiguration.dohIpv6Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *doh = [QNInternalDns dnsWithResolver:dohResolver];
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:doh error:&err];
|
||||
if (error != nil && err) {
|
||||
*error = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return [self getInetAddressByHost:host].firstObject.sourceValue;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prepareToPreFetch {
|
||||
if ([self isDnsOpen] == NO) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.lastPrefetchedErrorMessage = nil;
|
||||
|
||||
if (self.isPrefetching == YES) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self clearDnsCacheIfNeeded];
|
||||
|
||||
self.isPrefetching = YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)endPreFetch{
|
||||
self.isPrefetching = NO;
|
||||
}
|
||||
|
||||
- (void)preFetchHosts:(NSArray <NSString *> *)fetchHosts {
|
||||
NSError *err = nil;
|
||||
[self preFetchHosts:fetchHosts error:&err];
|
||||
self.lastPrefetchedErrorMessage = err.description;
|
||||
}
|
||||
|
||||
- (void)preFetchHosts:(NSArray <NSString *> *)fetchHosts error:(NSError **)error {
|
||||
NSArray *nextFetchHosts = fetchHosts;
|
||||
|
||||
// 定制
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:self.customDns error:error];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 系统
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:self.systemDns error:error];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// doh
|
||||
if (kQNGlobalConfiguration.dohEnable) {
|
||||
if (kQNGlobalConfiguration.dohIpv4Servers && [kQNGlobalConfiguration.dohIpv4Servers count] > 0) {
|
||||
QNDohResolver *dohResolver = [QNDohResolver resolverWithServers:kQNGlobalConfiguration.dohIpv4Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *doh = [QNInternalDns dnsWithResolver:dohResolver];
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:doh error:error];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ([QNIP isIpV6FullySupported] && kQNGlobalConfiguration.dohIpv6Servers && [kQNGlobalConfiguration.dohIpv6Servers count] > 0) {
|
||||
QNDohResolver *dohResolver = [QNDohResolver resolverWithServers:kQNGlobalConfiguration.dohIpv6Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *doh = [QNInternalDns dnsWithResolver:dohResolver];
|
||||
nextFetchHosts = [self preFetchHosts:nextFetchHosts dns:doh error:error];
|
||||
if (nextFetchHosts.count == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// udp
|
||||
if (kQNGlobalConfiguration.udpDnsEnable) {
|
||||
if (kQNGlobalConfiguration.udpDnsIpv4Servers && [kQNGlobalConfiguration.udpDnsIpv4Servers count] > 0) {
|
||||
QNDnsUdpResolver *udpDnsResolver = [QNDnsUdpResolver resolverWithServerIPs:kQNGlobalConfiguration.udpDnsIpv4Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *udpDns = [QNInternalDns dnsWithResolver:udpDnsResolver];
|
||||
[self preFetchHosts:nextFetchHosts dns:udpDns error:error];
|
||||
}
|
||||
|
||||
if ([QNIP isIpV6FullySupported] && kQNGlobalConfiguration.udpDnsIpv6Servers && [kQNGlobalConfiguration.udpDnsIpv6Servers count] > 0) {
|
||||
QNDnsUdpResolver *udpDnsResolver = [QNDnsUdpResolver resolverWithServerIPs:kQNGlobalConfiguration.udpDnsIpv6Servers recordType:kQNTypeA timeout:kQNGlobalConfiguration.dnsResolveTimeout];
|
||||
QNInternalDns *udpDns = [QNInternalDns dnsWithResolver:udpDnsResolver];
|
||||
[self preFetchHosts:nextFetchHosts dns:udpDns error:error];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)preFetchHosts:(NSArray <NSString *> *)preHosts dns:(QNInternalDns *)dns error:(NSError **)error {
|
||||
|
||||
if (!preHosts || preHosts.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!dns) {
|
||||
return [preHosts copy];
|
||||
}
|
||||
|
||||
int dnsRepreHostNum = kQNGlobalConfiguration.dnsRepreHostNum;
|
||||
NSMutableArray *failHosts = [NSMutableArray array];
|
||||
for (NSString *host in preHosts) {
|
||||
int rePreNum = 0;
|
||||
BOOL isSuccess = NO;
|
||||
|
||||
while (rePreNum < dnsRepreHostNum) {
|
||||
if ([self preFetchHost:host dns:dns error:error]) {
|
||||
isSuccess = YES;
|
||||
break;
|
||||
}
|
||||
rePreNum += 1;
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
[failHosts addObject:host];
|
||||
}
|
||||
}
|
||||
return [failHosts copy];
|
||||
}
|
||||
|
||||
- (BOOL)preFetchHost:(NSString *)preHost dns:(QNInternalDns *)dns error:(NSError **)error {
|
||||
|
||||
if (!preHost || preHost.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *addressDictionary = nil;
|
||||
@synchronized (self) {
|
||||
addressDictionary = [self.addressDictionary copy];
|
||||
}
|
||||
NSArray<QNDnsNetworkAddress *>* preAddressList = addressDictionary[preHost];
|
||||
if (preAddressList && ![preAddressList.firstObject needRefresh]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSArray <id <QNIDnsNetworkAddress> > * addressList = [dns query:preHost error:error];
|
||||
if (addressList && addressList.count > 0) {
|
||||
NSMutableArray *addressListP = [NSMutableArray array];
|
||||
for (id <QNIDnsNetworkAddress>inetAddress in addressList) {
|
||||
QNDnsNetworkAddress *address = [QNDnsNetworkAddress inetAddress:inetAddress];
|
||||
if (address) {
|
||||
address.hostValue = preHost;
|
||||
if (!address.ttlValue) {
|
||||
address.ttlValue = @(kQNDefaultDnsCacheTime);
|
||||
}
|
||||
if (!address.timestampValue) {
|
||||
address.timestampValue = @([[NSDate date] timeIntervalSince1970]);
|
||||
}
|
||||
[addressListP addObject:address];
|
||||
}
|
||||
}
|
||||
addressListP = [addressListP copy];
|
||||
@synchronized (self) {
|
||||
self.addressDictionary[preHost] = addressListP;
|
||||
}
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -- 加载和存储缓存信息
|
||||
- (BOOL)recoverDnsCache:(NSDictionary *)dataDic{
|
||||
if (dataDic == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *records = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in dataDic.allKeys) {
|
||||
NSArray *ips = dataDic[key];
|
||||
if ([ips isKindOfClass:[NSArray class]]) {
|
||||
|
||||
NSMutableArray <QNDnsNetworkAddress *> * addressList = [NSMutableArray array];
|
||||
|
||||
for (NSDictionary *ipInfo in ips) {
|
||||
if ([ipInfo isKindOfClass:[NSDictionary class]]) {
|
||||
QNDnsNetworkAddress *address = [QNDnsNetworkAddress inetAddress:ipInfo];
|
||||
if (address) {
|
||||
[addressList addObject:address];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addressList.count > 0) {
|
||||
records[key] = [addressList copy];
|
||||
}
|
||||
}
|
||||
}
|
||||
@synchronized (self) {
|
||||
[self.addressDictionary setValuesForKeysWithDictionary:records];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)recorderDnsCache{
|
||||
NSTimeInterval currentTime = [QNUtils currentTimestamp];
|
||||
NSString *localIp = [QNIP local];
|
||||
|
||||
if (localIp == nil || localIp.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
id <QNRecorderDelegate> recorder = [QNDnsCacheFile dnsCacheFile:kQNGlobalConfiguration.dnsCacheDir
|
||||
error:&error];
|
||||
if (error) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *addressDictionary = nil;
|
||||
@synchronized (self) {
|
||||
addressDictionary = [self.addressDictionary copy];
|
||||
}
|
||||
NSMutableDictionary *addressInfo = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in addressDictionary.allKeys) {
|
||||
|
||||
NSArray *addressModelList = addressDictionary[key];
|
||||
NSMutableArray * addressDicList = [NSMutableArray array];
|
||||
|
||||
for (QNDnsNetworkAddress *ipInfo in addressModelList) {
|
||||
NSDictionary *addressDic = [ipInfo toDictionary];
|
||||
if (addressDic) {
|
||||
[addressDicList addObject:addressDic];
|
||||
}
|
||||
}
|
||||
|
||||
if (addressDicList.count > 0) {
|
||||
addressInfo[key] = addressDicList;
|
||||
}
|
||||
}
|
||||
|
||||
QNDnsCacheInfo *cacheInfo = [QNDnsCacheInfo dnsCacheInfo:[NSString stringWithFormat:@"%.0lf",currentTime]
|
||||
localIp:localIp
|
||||
info:addressInfo];
|
||||
|
||||
NSData *cacheData = [cacheInfo jsonData];
|
||||
if (!cacheData) {
|
||||
return NO;
|
||||
}
|
||||
[self setDnsCacheInfo:cacheInfo];
|
||||
[recorder set:localIp data:cacheData];
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)clearDnsCacheIfNeeded{
|
||||
NSString *localIp = [QNIP local];
|
||||
if (localIp == nil || (self.dnsCacheInfo && ![localIp isEqualToString:self.dnsCacheInfo.localIp])) {
|
||||
[self clearDnsMemoryCache];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearDnsMemoryCache {
|
||||
@synchronized (self) {
|
||||
[self.addressDictionary removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearDnsDiskCache:(NSError **)error {
|
||||
[self.diskCache clearCache:error];
|
||||
}
|
||||
|
||||
|
||||
//MARK: -- 获取预取hosts
|
||||
- (NSArray <NSString *> *)getLocalPreHost{
|
||||
NSMutableArray *localHosts = [NSMutableArray array];
|
||||
[localHosts addObject:kQNUpLogHost];
|
||||
return [localHosts copy];
|
||||
}
|
||||
|
||||
- (NSArray <NSString *> *)getCurrentZoneHosts:(QNConfiguration *)config
|
||||
zone:(QNZone *)currentZone
|
||||
token:(QNUpToken *)token{
|
||||
if (!currentZone || !token || !token.token) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block QNZonesInfo *zonesInfo = nil;
|
||||
[currentZone query:config token:token on:^(QNResponseInfo * _Nullable httpResponseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, QNZonesInfo * _Nullable info) {
|
||||
zonesInfo = info;
|
||||
dispatch_semaphore_signal(self.semaphore);
|
||||
}];
|
||||
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (zonesInfo == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
QNZonesInfo *autoZonesInfo = [currentZone getZonesInfoWithToken:token];
|
||||
NSMutableArray *autoHosts = [NSMutableArray array];
|
||||
NSArray *zoneInfoList = autoZonesInfo.zonesInfo;
|
||||
for (QNZoneInfo *info in zoneInfoList) {
|
||||
if (info.allHosts) {
|
||||
[autoHosts addObjectsFromArray:info.allHosts];
|
||||
}
|
||||
}
|
||||
return [autoHosts copy];
|
||||
}
|
||||
|
||||
|
||||
//MARK: --
|
||||
- (BOOL)isDnsOpen{
|
||||
return [kQNGlobalConfiguration isDnsOpen];
|
||||
}
|
||||
|
||||
- (QNDnsCacheInfo *)dnsCacheInfo{
|
||||
if (_dnsCacheInfo == nil) {
|
||||
_dnsCacheInfo = [[QNDnsCacheInfo alloc] init];
|
||||
}
|
||||
return _dnsCacheInfo;
|
||||
}
|
||||
- (NSMutableDictionary<NSString *,NSArray<QNDnsNetworkAddress *> *> *)addressDictionary{
|
||||
if (_addressDictionary == nil) {
|
||||
_addressDictionary = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return _addressDictionary;
|
||||
}
|
||||
|
||||
- (dispatch_semaphore_t)semaphore{
|
||||
if (_getAutoZoneSemaphore == NULL) {
|
||||
_getAutoZoneSemaphore = dispatch_semaphore_create(0);
|
||||
}
|
||||
return _getAutoZoneSemaphore;
|
||||
}
|
||||
|
||||
- (QNDnsCacheFile *)diskCache {
|
||||
if (!_diskCache) {
|
||||
NSError *error;
|
||||
QNDnsCacheFile *cache = [QNDnsCacheFile dnsCacheFile:kQNGlobalConfiguration.dnsCacheDir error:&error];
|
||||
if (!error) {
|
||||
_diskCache = cache;
|
||||
}
|
||||
}
|
||||
return _diskCache;
|
||||
}
|
||||
|
||||
- (QNInternalDns *)customDns {
|
||||
if (_customDns == nil && kQNGlobalConfiguration.dns) {
|
||||
_customDns = [QNInternalDns dnsWithDns:kQNGlobalConfiguration.dns];
|
||||
}
|
||||
return _customDns;
|
||||
}
|
||||
|
||||
- (QNInternalDns *)systemDns {
|
||||
if (_systemDns == nil) {
|
||||
_systemDns = [QNInternalDns dnsWithResolver:[[QNResolver alloc] initWithAddress:nil timeout:self.dnsPrefetchTimeout]];
|
||||
}
|
||||
return _systemDns;
|
||||
}
|
||||
|
||||
- (NSMutableSet *)prefetchHosts {
|
||||
if (!_prefetchHosts) {
|
||||
_prefetchHosts = [NSMutableSet set];
|
||||
}
|
||||
return _prefetchHosts;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//MARK: -- DNS 事务
|
||||
@implementation QNTransactionManager(Dns)
|
||||
#define kQNLoadLocalDnsTransactionName @"QNLoadLocalDnsTransaction"
|
||||
#define kQNDnsCheckAndPrefetchTransactionName @"QNDnsCheckAndPrefetchTransactionName"
|
||||
|
||||
- (void)addDnsLocalLoadTransaction{
|
||||
|
||||
if ([kQNDnsPrefetch isDnsOpen] == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
QNTransaction *transaction = [QNTransaction transaction:kQNLoadLocalDnsTransactionName after:0 action:^{
|
||||
|
||||
[kQNDnsPrefetch recoverCache];
|
||||
[kQNDnsPrefetch localFetch];
|
||||
}];
|
||||
[[QNTransactionManager shared] addTransaction:transaction];
|
||||
[self setDnsCheckWhetherCachedValidTransactionAction];
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)addDnsCheckAndPrefetchTransaction:(QNZone *)currentZone token:(QNUpToken *)token {
|
||||
return [self addDnsCheckAndPrefetchTransaction:[QNConfiguration defaultConfiguration] zone:currentZone token:token];
|
||||
}
|
||||
|
||||
- (BOOL)addDnsCheckAndPrefetchTransaction:(QNConfiguration *)config zone:(QNZone *)currentZone token:(QNUpToken *)token {
|
||||
|
||||
if (!token) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([kQNDnsPrefetch isDnsOpen] == NO) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL ret = NO;
|
||||
@synchronized (kQNDnsPrefetch) {
|
||||
|
||||
QNTransactionManager *transactionManager = [QNTransactionManager shared];
|
||||
|
||||
if (![transactionManager existTransactionsForName:token.token]) {
|
||||
QNTransaction *transaction = [QNTransaction transaction:token.token after:0 action:^{
|
||||
|
||||
[kQNDnsPrefetch checkAndPrefetchDnsIfNeed:config zone:currentZone token:token];
|
||||
}];
|
||||
[transactionManager addTransaction:transaction];
|
||||
|
||||
ret = YES;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)setDnsCheckWhetherCachedValidTransactionAction{
|
||||
|
||||
if ([kQNDnsPrefetch isDnsOpen] == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized (kQNDnsPrefetch) {
|
||||
|
||||
QNTransactionManager *transactionManager = [QNTransactionManager shared];
|
||||
QNTransaction *transaction = [transactionManager transactionsForName:kQNDnsCheckAndPrefetchTransactionName].firstObject;
|
||||
|
||||
if (!transaction) {
|
||||
|
||||
QNTransaction *transaction = [QNTransaction timeTransaction:kQNDnsCheckAndPrefetchTransactionName
|
||||
after:10
|
||||
interval:120
|
||||
action:^{
|
||||
[kQNDnsPrefetch checkWhetherCachedDnsValid];
|
||||
}];
|
||||
[transactionManager addTransaction:transaction];
|
||||
} else {
|
||||
[transactionManager performTransaction:transaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BOOL kQNIsDnsSourceDoh(NSString * _Nullable source) {
|
||||
return [source containsString:kQNDnsSourceDoh];
|
||||
}
|
||||
|
||||
BOOL kQNIsDnsSourceUdp(NSString * _Nullable source) {
|
||||
return [source containsString:kQNDnsSourceUdp];
|
||||
}
|
||||
|
||||
BOOL kQNIsDnsSourceDnsPod(NSString * _Nullable source) {
|
||||
return [source containsString:kQNDnsSourceDnspod];
|
||||
}
|
||||
|
||||
BOOL kQNIsDnsSourceSystem(NSString * _Nullable source) {
|
||||
return [source containsString:kQNDnsSourceSystem];
|
||||
}
|
||||
|
||||
BOOL kQNIsDnsSourceCustom(NSString * _Nullable source) {
|
||||
return [source containsString:kQNDnsSourceCustom];
|
||||
}
|
||||
37
Pods/Qiniu/QiniuSDK/Http/Dns/QNInetAddress.h
generated
Normal file
37
Pods/Qiniu/QiniuSDK/Http/Dns/QNInetAddress.h
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// QNInetAddress.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by 杨森 on 2020/7/27.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDns.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNInetAddress : NSObject <QNIDnsNetworkAddress>
|
||||
|
||||
@property(nonatomic, copy)NSString *hostValue;
|
||||
@property(nonatomic, copy)NSString *ipValue;
|
||||
@property(nonatomic, strong)NSNumber *ttlValue;
|
||||
@property(nonatomic, strong)NSNumber *timestampValue;
|
||||
@property(nonatomic, copy)NSString *sourceValue;
|
||||
|
||||
|
||||
/// 构造方法 addressData为json
|
||||
/// @param addressInfo 地址信息,类型可能为String / Dictionary / Data / 遵循 QNInetAddressDelegate的实例
|
||||
+ (instancetype)inetAddress:(id)addressInfo;
|
||||
|
||||
/// 是否有效,根据时间戳判断
|
||||
- (BOOL)isValid;
|
||||
|
||||
/// 对象转json
|
||||
- (NSString *)toJsonInfo;
|
||||
|
||||
/// 对象转字典
|
||||
- (NSDictionary *)toDictionary;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
95
Pods/Qiniu/QiniuSDK/Http/Dns/QNInetAddress.m
generated
Normal file
95
Pods/Qiniu/QiniuSDK/Http/Dns/QNInetAddress.m
generated
Normal file
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// QNInetAddress.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by 杨森 on 2020/7/27.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNInetAddress.h"
|
||||
|
||||
@interface QNInetAddress()
|
||||
@end
|
||||
@implementation QNInetAddress
|
||||
+ (instancetype)inetAddress:(id)addressInfo{
|
||||
|
||||
NSDictionary *addressDic = nil;
|
||||
if ([addressInfo isKindOfClass:[NSDictionary class]]) {
|
||||
addressDic = (NSDictionary *)addressInfo;
|
||||
} else if ([addressInfo isKindOfClass:[NSString class]]){
|
||||
NSData *data = [(NSString *)addressInfo dataUsingEncoding:NSUTF8StringEncoding];
|
||||
addressDic = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:NSJSONReadingMutableLeaves
|
||||
error:nil];
|
||||
} else if ([addressInfo isKindOfClass:[NSData class]]) {
|
||||
addressDic = [NSJSONSerialization JSONObjectWithData:(NSData *)addressInfo
|
||||
options:NSJSONReadingMutableLeaves
|
||||
error:nil];
|
||||
} else if ([addressInfo conformsToProtocol:@protocol(QNIDnsNetworkAddress)]){
|
||||
id <QNIDnsNetworkAddress> address = (id <QNIDnsNetworkAddress> )addressInfo;
|
||||
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
||||
if ([address respondsToSelector:@selector(hostValue)] && [address hostValue]) {
|
||||
dic[@"hostValue"] = [address hostValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(ipValue)] && [address ipValue]) {
|
||||
dic[@"ipValue"] = [address ipValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(ttlValue)] && [address ttlValue]) {
|
||||
dic[@"ttlValue"] = [address ttlValue];
|
||||
}
|
||||
if ([address respondsToSelector:@selector(timestampValue)] && [address timestampValue]) {
|
||||
dic[@"timestampValue"] = [address timestampValue];
|
||||
}
|
||||
addressDic = [dic copy];
|
||||
}
|
||||
|
||||
if (addressDic) {
|
||||
QNInetAddress *address = [[QNInetAddress alloc] init];
|
||||
[address setValuesForKeysWithDictionary:addressDic];
|
||||
return address;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isValid{
|
||||
if (!self.timestampValue || !self.ipValue || self.ipValue.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
|
||||
if (currentTimestamp > self.timestampValue.doubleValue + self.ttlValue.doubleValue) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)toJsonInfo{
|
||||
NSString *defaultString = @"{}";
|
||||
NSDictionary *infoDic = [self toDictionary];
|
||||
if (!infoDic) {
|
||||
return defaultString;
|
||||
}
|
||||
|
||||
NSData *infoData = [NSJSONSerialization dataWithJSONObject:infoDic
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
if (!infoData) {
|
||||
return defaultString;
|
||||
}
|
||||
|
||||
NSString *infoStr = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding];
|
||||
if (!infoStr) {
|
||||
return defaultString;
|
||||
} else {
|
||||
return infoStr;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)toDictionary{
|
||||
return [self dictionaryWithValuesForKeys:@[@"ipValue", @"hostValue", @"ttlValue", @"timestampValue"]];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
|
||||
|
||||
@end
|
||||
37
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNNetworkStatusManager.h
generated
Normal file
37
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNNetworkStatusManager.h
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// QNNetworkStatusManager.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNNetworkStatus : NSObject
|
||||
|
||||
/// 网速 单位:kb/s 默认:200kb/s
|
||||
@property(nonatomic, assign, readonly)int speed;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#define kQNNetworkStatusManager [QNNetworkStatusManager sharedInstance]
|
||||
@interface QNNetworkStatusManager : NSObject
|
||||
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
+ (NSString *)getNetworkStatusType:(NSString *)host
|
||||
ip:(NSString *)ip;
|
||||
|
||||
- (QNNetworkStatus *)getNetworkStatus:(NSString *)type;
|
||||
|
||||
- (void)updateNetworkStatus:(NSString *)type
|
||||
speed:(int)speed;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
93
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNNetworkStatusManager.m
generated
Normal file
93
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNNetworkStatusManager.m
generated
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// QNNetworkStatusManager.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUtils.h"
|
||||
#import "QNCache.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNFileRecorder.h"
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import "QNNetworkStatusManager.h"
|
||||
|
||||
@interface QNNetworkStatus()<QNCacheObject>
|
||||
@property(nonatomic, assign)int speed;
|
||||
@end
|
||||
@implementation QNNetworkStatus
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_speed = 200;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nonnull id<QNCacheObject>)initWithDictionary:(nullable NSDictionary *)dictionary {
|
||||
QNNetworkStatus *status = [[QNNetworkStatus alloc] init];
|
||||
status.speed = [dictionary[@"speed"] intValue];
|
||||
return status;
|
||||
}
|
||||
|
||||
- (NSDictionary *)toDictionary{
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:@(self.speed) forKey:@"speed"];
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
+ (QNNetworkStatus *)statusFromDictionary:(NSDictionary *)dictionary{
|
||||
QNNetworkStatus *status = [[QNNetworkStatus alloc] init];
|
||||
status.speed = [dictionary[@"speed"] intValue];
|
||||
return status;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface QNNetworkStatusManager()
|
||||
|
||||
@property(nonatomic, strong)QNCache *cache;
|
||||
|
||||
@end
|
||||
@implementation QNNetworkStatusManager
|
||||
|
||||
+ (instancetype)sharedInstance{
|
||||
static QNNetworkStatusManager *manager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[QNNetworkStatusManager alloc] init];
|
||||
[manager initData];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
- (void)initData{
|
||||
QNCacheOption *option = [[QNCacheOption alloc] init];
|
||||
option.version = @"v1.0.2";
|
||||
option.flushCount = 10;
|
||||
self.cache = [QNCache cache:[QNNetworkStatus class] option:option];
|
||||
}
|
||||
|
||||
+ (NSString *)getNetworkStatusType:(NSString *)host
|
||||
ip:(NSString *)ip {
|
||||
return [QNUtils getIpType:ip host:host];
|
||||
}
|
||||
|
||||
- (QNNetworkStatus *)getNetworkStatus:(NSString *)type{
|
||||
if (type == nil || type.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
return [self.cache cacheForKey:type];
|
||||
}
|
||||
|
||||
- (void)updateNetworkStatus:(NSString *)type speed:(int)speed{
|
||||
if (type == nil || type.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QNNetworkStatus *status = [[QNNetworkStatus alloc] init];
|
||||
status.speed = speed;
|
||||
[self.cache cache:status forKey:type atomically:false];
|
||||
}
|
||||
|
||||
@end
|
||||
24
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNUploadServerNetworkStatus.h
generated
Normal file
24
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNUploadServerNetworkStatus.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// QNUploadServerNetworkStatus.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNUploadServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadServerNetworkStatus : NSObject
|
||||
|
||||
+ (QNUploadServer *)getBetterNetworkServer:(QNUploadServer *)serverA
|
||||
serverB:(QNUploadServer *)serverB;
|
||||
|
||||
+ (BOOL)isServerNetworkBetter:(QNUploadServer *)serverA
|
||||
thanServerB:(QNUploadServer *)serverB;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
40
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNUploadServerNetworkStatus.m
generated
Normal file
40
Pods/Qiniu/QiniuSDK/Http/NetworkStatus/QNUploadServerNetworkStatus.m
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// QNUploadServerNetworkStatus.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUtils.h"
|
||||
#import "QNNetworkStatusManager.h"
|
||||
#import "QNUploadServerNetworkStatus.h"
|
||||
|
||||
@implementation QNUploadServerNetworkStatus
|
||||
|
||||
+ (QNUploadServer *)getBetterNetworkServer:(QNUploadServer *)serverA serverB:(QNUploadServer *)serverB {
|
||||
return [self isServerNetworkBetter:serverA thanServerB:serverB] ? serverA : serverB;
|
||||
}
|
||||
|
||||
+ (BOOL)isServerNetworkBetter:(QNUploadServer *)serverA thanServerB:(QNUploadServer *)serverB {
|
||||
if (serverA == nil) {
|
||||
return NO;
|
||||
} else if (serverB == nil) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSString *serverTypeA = [QNNetworkStatusManager getNetworkStatusType:serverA.host ip:serverA.ip];
|
||||
NSString *serverTypeB = [QNNetworkStatusManager getNetworkStatusType:serverB.host ip:serverB.ip];
|
||||
if (serverTypeA == nil) {
|
||||
return NO;
|
||||
} else if (serverTypeB == nil) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
QNNetworkStatus *serverStatusA = [kQNNetworkStatusManager getNetworkStatus:serverTypeA];
|
||||
QNNetworkStatus *serverStatusB = [kQNNetworkStatusManager getNetworkStatus:serverTypeB];
|
||||
|
||||
return serverStatusB.speed < serverStatusA.speed;
|
||||
}
|
||||
|
||||
@end
|
||||
85
Pods/Qiniu/QiniuSDK/Http/QNResponseInfo.h
generated
Executable file
85
Pods/Qiniu/QiniuSDK/Http/QNResponseInfo.h
generated
Executable file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// QNResponseInfo.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/2.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNErrorCode.h"
|
||||
|
||||
/**
|
||||
* 上传完成后返回的状态信息
|
||||
*/
|
||||
@interface QNResponseInfo : NSObject
|
||||
|
||||
/// 状态码,具体见 QNErrorCode.h
|
||||
@property (readonly) int statusCode;
|
||||
/// response 信息
|
||||
@property (nonatomic, copy, readonly) NSDictionary *responseDictionary;
|
||||
/// response 头信息
|
||||
@property (nonatomic, copy, readonly) NSDictionary *responseHeader;
|
||||
/// response message
|
||||
@property (nonatomic, copy, readonly) NSString *message;
|
||||
/// 七牛服务器生成的请求ID,用来跟踪请求信息,如果使用过程中出现问题,请反馈此ID
|
||||
@property (nonatomic, copy, readonly) NSString *reqId;
|
||||
/// 七牛服务器内部跟踪记录
|
||||
@property (nonatomic, copy, readonly) NSString *xlog;
|
||||
/// cdn服务器内部跟踪记录
|
||||
@property (nonatomic, copy, readonly) NSString *xvia;
|
||||
/// 错误信息,出错时请反馈此记录
|
||||
@property (nonatomic, copy, readonly) NSError *error;
|
||||
/// 服务器域名
|
||||
@property (nonatomic, copy, readonly) NSString *host;
|
||||
/// 客户端id
|
||||
@property (nonatomic, readonly) NSString *id;
|
||||
/// 时间戳
|
||||
@property (readonly) UInt64 timeStamp;
|
||||
/// 是否取消
|
||||
@property (nonatomic, readonly, getter=isCancelled) BOOL canceled;
|
||||
/// 成功的请求
|
||||
@property (nonatomic, readonly, getter=isOK) BOOL ok;
|
||||
/// 是否网络错误
|
||||
@property (nonatomic, readonly, getter=isConnectionBroken) BOOL broken;
|
||||
/// 是否TLS错误
|
||||
@property (nonatomic, readonly) BOOL isTlsError;
|
||||
/// 是否可以再次重试,当遇到权限等怎么重试都不可能成功的问题时,返回NO
|
||||
@property (nonatomic, readonly) BOOL couldRetry;
|
||||
/// 单个host是否可以再次重试
|
||||
@property (nonatomic, readonly) BOOL couldHostRetry;
|
||||
/// 单个Region是否可以再次重试
|
||||
@property (nonatomic, readonly) BOOL couldRegionRetry;
|
||||
/// 当前host是否可达
|
||||
@property (nonatomic, readonly) BOOL canConnectToHost;
|
||||
/// 当前host是否可用
|
||||
@property (nonatomic, readonly) BOOL isHostUnavailable;
|
||||
/// 在断点续上传过程中,ctx 信息已过期。
|
||||
@property (nonatomic, readonly) BOOL isCtxExpiedError;
|
||||
/// 是否是加速配置错误
|
||||
@property (nonatomic, readonly) BOOL isTransferAccelerationConfigureError;
|
||||
/// 是否为 七牛响应
|
||||
@property (nonatomic, readonly, getter=isNotQiniu) BOOL notQiniu;
|
||||
|
||||
//MARK:-- 构造函数 【内部使用】
|
||||
+ (instancetype)successResponse;
|
||||
+ (instancetype)successResponseWithDesc:(NSString *)desc;
|
||||
+ (instancetype)cancelResponse;
|
||||
+ (instancetype)responseInfoWithNetworkError:(NSString *)desc;
|
||||
+ (instancetype)responseInfoWithInvalidArgument:(NSString *)desc;
|
||||
+ (instancetype)responseInfoWithInvalidToken:(NSString *)desc;
|
||||
+ (instancetype)responseInfoWithFileError:(NSError *)error;
|
||||
+ (instancetype)responseInfoOfZeroData:(NSString *)path;
|
||||
+ (instancetype)responseInfoWithLocalIOError:(NSString *)desc;
|
||||
+ (instancetype)responseInfoWithMaliciousResponseError:(NSString *)desc;
|
||||
// 使用responseInfoWithSDKInteriorError替代
|
||||
+ (instancetype)responseInfoWithNoUsableHostError:(NSString *)desc NS_UNAVAILABLE;
|
||||
+ (instancetype)responseInfoWithSDKInteriorError:(NSString *)desc;
|
||||
+ (instancetype)responseInfoWithUnexpectedSysCallError:(NSString *)desc;
|
||||
|
||||
+ (instancetype)errorResponseInfo:(int)errorType
|
||||
errorDesc:(NSString *)errorDesc;
|
||||
- (instancetype)initWithResponseInfoHost:(NSString *)host
|
||||
response:(NSHTTPURLResponse *)response
|
||||
body:(NSData *)body
|
||||
error:(NSError *)error;
|
||||
@end
|
||||
341
Pods/Qiniu/QiniuSDK/Http/QNResponseInfo.m
generated
Executable file
341
Pods/Qiniu/QiniuSDK/Http/QNResponseInfo.m
generated
Executable file
@@ -0,0 +1,341 @@
|
||||
//
|
||||
// QNResponseInfo.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/2.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
#import "QNErrorCode.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUserAgent.h"
|
||||
#import "QNUtils.h"
|
||||
|
||||
static NSString *kQNErrorDomain = @"qiniu.com";
|
||||
|
||||
@interface QNResponseInfo ()
|
||||
|
||||
@property (assign) int statusCode;
|
||||
@property (nonatomic, copy) NSString *message;
|
||||
@property (nonatomic, copy) NSString *reqId;
|
||||
@property (nonatomic, copy) NSString *xlog;
|
||||
@property (nonatomic, copy) NSString *xvia;
|
||||
@property (nonatomic, copy) NSError *error;
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
@property (nonatomic, copy) NSString *id;
|
||||
@property (assign) UInt64 timeStamp;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNResponseInfo
|
||||
+ (instancetype)successResponse{
|
||||
return [QNResponseInfo successResponseWithDesc:@"inter:ok"];
|
||||
}
|
||||
|
||||
+ (instancetype)successResponseWithDesc:(NSString *)desc {
|
||||
QNResponseInfo *responseInfo = [[QNResponseInfo alloc] init];
|
||||
responseInfo.statusCode = 200;
|
||||
responseInfo.message = desc;
|
||||
responseInfo.xlog = @"inter:xlog";
|
||||
responseInfo.reqId = @"inter:reqid";
|
||||
return responseInfo;
|
||||
}
|
||||
|
||||
+ (instancetype)cancelResponse {
|
||||
return [QNResponseInfo errorResponseInfo:kQNRequestCancelled
|
||||
errorDesc:@"cancelled by user"];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithNetworkError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNNetworkError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithInvalidArgument:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNInvalidArgument
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithInvalidToken:(NSString *)desc {
|
||||
return [QNResponseInfo errorResponseInfo:kQNInvalidToken
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithFileError:(NSError *)error {
|
||||
return [QNResponseInfo errorResponseInfo:kQNFileError
|
||||
errorDesc:nil
|
||||
error:error];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoOfZeroData:(NSString *)path {
|
||||
NSString *desc;
|
||||
if (path == nil) {
|
||||
desc = @"data size is 0";
|
||||
} else {
|
||||
desc = [[NSString alloc] initWithFormat:@"file %@ size is 0", path];
|
||||
}
|
||||
return [QNResponseInfo errorResponseInfo:kQNZeroDataSize
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithLocalIOError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNLocalIOError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithMaliciousResponseError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNMaliciousResponseError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithNoUsableHostError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNSDKInteriorError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithSDKInteriorError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNSDKInteriorError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)responseInfoWithUnexpectedSysCallError:(NSString *)desc{
|
||||
return [QNResponseInfo errorResponseInfo:kQNUnexpectedSysCallError
|
||||
errorDesc:desc];
|
||||
}
|
||||
|
||||
+ (instancetype)errorResponseInfo:(int)errorType
|
||||
errorDesc:(NSString *)errorDesc{
|
||||
return [self errorResponseInfo:errorType errorDesc:errorDesc error:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)errorResponseInfo:(int)errorType
|
||||
errorDesc:(NSString *)errorDesc
|
||||
error:(NSError *)error{
|
||||
QNResponseInfo *response = [[QNResponseInfo alloc] init];
|
||||
response.statusCode = errorType;
|
||||
response.message = errorDesc;
|
||||
if (error) {
|
||||
response.error = error;
|
||||
} else {
|
||||
NSError *error = [[NSError alloc] initWithDomain:kQNErrorDomain
|
||||
code:errorType
|
||||
userInfo:@{ @"error" : response.message ?: @"error" }];
|
||||
response.error = error;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResponseInfoHost:(NSString *)host
|
||||
response:(NSHTTPURLResponse *)response
|
||||
body:(NSData *)body
|
||||
error:(NSError *)error {
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
_host = host;
|
||||
_timeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
|
||||
if (response) {
|
||||
|
||||
int statusCode = (int)[response statusCode];
|
||||
NSDictionary *headers = [response allHeaderFields];
|
||||
_responseHeader = [headers copy];
|
||||
_statusCode = statusCode;
|
||||
_reqId = headers[@"x-reqid"];
|
||||
_xlog = headers[@"x-log"];
|
||||
_xvia = headers[@"x-via"] ?: headers[@"x-px"] ?: headers[@"fw-via"];
|
||||
if (_statusCode == 200 && _reqId == nil && _xlog == nil) {
|
||||
_statusCode = kQNMaliciousResponseError;
|
||||
_message = @"this is a malicious response";
|
||||
_responseDictionary = nil;
|
||||
_error = [[NSError alloc] initWithDomain:kQNErrorDomain code:_statusCode userInfo:@{@"error" : _message}];
|
||||
} else if (error) {
|
||||
_error = error;
|
||||
_statusCode = (int)error.code;
|
||||
_message = [NSString stringWithFormat:@"%@", error];
|
||||
_responseDictionary = nil;
|
||||
} else {
|
||||
NSMutableDictionary *errorUserInfo = [@{@"errorHost" : host ?: @""} mutableCopy];
|
||||
if (!body) {
|
||||
_message = @"no response data";
|
||||
_error = nil;
|
||||
_responseDictionary = nil;
|
||||
} else {
|
||||
NSError *tmp = nil;
|
||||
NSDictionary *responseInfo = nil;
|
||||
responseInfo = [NSJSONSerialization JSONObjectWithData:body options:NSJSONReadingMutableLeaves error:&tmp];
|
||||
if (tmp){
|
||||
_message = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
|
||||
_error = nil;
|
||||
_responseDictionary = nil;
|
||||
} else if (statusCode >= 200 && statusCode < 300) {
|
||||
_error = nil;
|
||||
_message = @"ok";
|
||||
_responseDictionary = responseInfo;
|
||||
} else {
|
||||
NSString *errorString = responseInfo[@"error"];
|
||||
if (errorString) {
|
||||
[errorUserInfo setDictionary:@{@"error" : errorString}];
|
||||
_message = errorString;
|
||||
_error = [[NSError alloc] initWithDomain:kQNErrorDomain code:statusCode userInfo:errorUserInfo];
|
||||
} else {
|
||||
_message = errorString;
|
||||
_error = nil;
|
||||
}
|
||||
|
||||
_responseDictionary = responseInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (error) {
|
||||
_error = error;
|
||||
_statusCode = (int)error.code;
|
||||
_message = [NSString stringWithFormat:@"%@", error];
|
||||
_responseDictionary = nil;
|
||||
} else {
|
||||
_statusCode = kQNUnexpectedSysCallError;
|
||||
_message = @"no response";
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCancelled {
|
||||
return _statusCode == kQNRequestCancelled || _statusCode == -999;
|
||||
}
|
||||
|
||||
- (BOOL)isTlsError{
|
||||
if (_statusCode == NSURLErrorServerCertificateHasBadDate
|
||||
|| _statusCode == NSURLErrorClientCertificateRejected
|
||||
|| _statusCode == NSURLErrorClientCertificateRequired) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isQiniu {
|
||||
// reqId is nill means the server is not qiniu
|
||||
return ![self isNotQiniu];
|
||||
}
|
||||
|
||||
- (BOOL)isNotQiniu {
|
||||
// reqId is nill means the server is not qiniu
|
||||
return (_statusCode == kQNMaliciousResponseError) || (_statusCode > 0 && _reqId == nil && _xlog == nil);
|
||||
}
|
||||
|
||||
- (BOOL)isOK {
|
||||
return (_statusCode >= 200 && _statusCode < 300) && _error == nil && (_reqId != nil || _xlog != nil);
|
||||
}
|
||||
|
||||
- (BOOL)couldRetry {
|
||||
if (![self isQiniu]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([self isCtxExpiedError]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([self isTransferAccelerationConfigureError]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (self.isCancelled
|
||||
|| _statusCode == 100
|
||||
|| (_statusCode > 300 && _statusCode < 400)
|
||||
|| (_statusCode > 400 && _statusCode < 500 && _statusCode != 406)
|
||||
|| _statusCode == 501 || _statusCode == 573
|
||||
|| _statusCode == 608 || _statusCode == 612 || _statusCode == 614 || _statusCode == 616
|
||||
|| _statusCode == 619 || _statusCode == 630 || _statusCode == 631 || _statusCode == 640
|
||||
|| (_statusCode != kQNLocalIOError && _statusCode != kQNUnexpectedSysCallError && _statusCode < -1 && _statusCode > -1000)) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)couldRegionRetry{
|
||||
if (![self isQiniu]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([self isTransferAccelerationConfigureError]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (self.isCancelled
|
||||
|| _statusCode == 100
|
||||
|| (_statusCode > 300 && _statusCode < 500 && _statusCode != 406)
|
||||
|| _statusCode == 501 || _statusCode == 573 || _statusCode == 579
|
||||
|| _statusCode == 608 || _statusCode == 612 || _statusCode == 614 || _statusCode == 616
|
||||
|| _statusCode == 619 || _statusCode == 630 || _statusCode == 631 || _statusCode == 640
|
||||
|| _statusCode == 701
|
||||
|| (_statusCode != kQNLocalIOError && _statusCode != kQNUnexpectedSysCallError && _statusCode < -1 && _statusCode > -1000)) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)couldHostRetry{
|
||||
if (![self isQiniu]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([self isTransferAccelerationConfigureError]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (self.isCancelled
|
||||
|| _statusCode == 100
|
||||
|| (_statusCode > 300 && _statusCode < 500 && _statusCode != 406)
|
||||
|| _statusCode == 501 || _statusCode == 502 || _statusCode == 503
|
||||
|| _statusCode == 571 || _statusCode == 573 || _statusCode == 579 || _statusCode == 599
|
||||
|| _statusCode == 608 || _statusCode == 612 || _statusCode == 614 || _statusCode == 616
|
||||
|| _statusCode == 619 || _statusCode == 630 || _statusCode == 631 || _statusCode == 640
|
||||
|| _statusCode == 701
|
||||
|| (_statusCode != kQNLocalIOError && _statusCode != kQNUnexpectedSysCallError && _statusCode < -1 && _statusCode > -1000)) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canConnectToHost{
|
||||
if (_statusCode > 99 || self.isCancelled) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isHostUnavailable{
|
||||
// 基本不可恢复,注:会影响下次请求,范围太大可能会造成大量的timeout
|
||||
if ([self isTransferAccelerationConfigureError] ||
|
||||
_statusCode == 502 || _statusCode == 503 || _statusCode == 504 || _statusCode == 599) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isCtxExpiedError {
|
||||
return _statusCode == 701 || (_statusCode == 612 && [_message containsString:@"no such uploadId"]);
|
||||
}
|
||||
|
||||
- (BOOL)isTransferAccelerationConfigureError {
|
||||
NSString *errorDesc = [NSString stringWithFormat:@"%@", self.error];
|
||||
return [errorDesc containsString:@"transfer acceleration is not configured on this bucket"];
|
||||
}
|
||||
|
||||
- (BOOL)isConnectionBroken {
|
||||
return _statusCode == kQNNetworkError || _statusCode == NSURLErrorNotConnectedToInternet;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@= id: %@, ver: %@, status: %d, requestId: %@, xlog: %@, xvia: %@, host: %@ time: %llu error: %@>", NSStringFromClass([self class]), _id, [QNUtils sdkVersion], _statusCode, _reqId, _xlog, _xvia, _host, _timeStamp, _error];
|
||||
}
|
||||
|
||||
@end
|
||||
128
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestMetrics.h
generated
Normal file
128
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestMetrics.h
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// QNUploadRequestMetrics.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNUploadRegionInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadMetrics : NSObject
|
||||
|
||||
@property (nonatomic, nullable, strong, readonly) NSDate *startDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSDate *endDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalElapsedTime;
|
||||
|
||||
//MARK:-- 构造
|
||||
+ (instancetype)emptyMetrics;
|
||||
|
||||
- (void)start;
|
||||
- (void)end;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#define kQNMetricsRequestHijacked @"forsure"
|
||||
#define kQNMetricsRequestMaybeHijacked @"maybe"
|
||||
|
||||
@interface QNUploadSingleRequestMetrics : QNUploadMetrics
|
||||
|
||||
// 请求的 httpVersion
|
||||
@property (nonatomic, copy)NSString *httpVersion;
|
||||
|
||||
// 请求是否劫持
|
||||
@property (nonatomic, copy)NSString *hijacked;
|
||||
@property (nonatomic, assign, readonly)BOOL isForsureHijacked;
|
||||
@property (nonatomic, assign, readonly)BOOL isMaybeHijacked;
|
||||
@property (nonatomic, copy) NSString *syncDnsSource;
|
||||
@property (nonatomic, strong) NSError *syncDnsError;
|
||||
|
||||
// 只有进行网络检测才会有 connectCheckMetrics
|
||||
@property (nonatomic, nullable , strong) QNUploadSingleRequestMetrics *connectCheckMetrics;
|
||||
|
||||
// 错误信息
|
||||
@property (nonatomic, nullable , strong) NSError *error;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSURLRequest *request;
|
||||
@property (nonatomic, nullable, copy) NSURLResponse *response;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSDate *domainLookupStartDate;
|
||||
@property (nonatomic, nullable, copy) NSDate *domainLookupEndDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalDnsTime;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSDate *connectStartDate;
|
||||
@property (nonatomic, nullable, copy) NSDate *connectEndDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalConnectTime;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSDate *secureConnectionStartDate;
|
||||
@property (nonatomic, nullable, copy) NSDate *secureConnectionEndDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalSecureConnectTime;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSDate *requestStartDate;
|
||||
@property (nonatomic, nullable, copy) NSDate *requestEndDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalRequestTime;
|
||||
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalWaitTime;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSDate *responseStartDate;
|
||||
@property (nonatomic, nullable, copy) NSDate *responseEndDate;
|
||||
@property (nonatomic, nullable, strong, readonly) NSNumber *totalResponseTime;
|
||||
|
||||
@property (nonatomic, assign) int64_t countOfRequestHeaderBytesSent;
|
||||
@property (nonatomic, assign) int64_t countOfRequestBodyBytesSent;
|
||||
|
||||
@property (nonatomic, assign) int64_t countOfResponseHeaderBytesReceived;
|
||||
@property (nonatomic, assign) int64_t countOfResponseBodyBytesReceived;
|
||||
|
||||
@property (nonatomic, nullable, copy) NSString *localAddress;
|
||||
@property (nonatomic, nullable, copy) NSNumber *localPort;
|
||||
@property (nonatomic, nullable, copy) NSString *remoteAddress;
|
||||
@property (nonatomic, nullable, copy) NSNumber *remotePort;
|
||||
|
||||
@property (nonatomic, strong, readonly) NSNumber *totalBytes;
|
||||
@property (nonatomic, strong, readonly) NSNumber *bytesSend;
|
||||
@property (nonatomic, strong, readonly) NSNumber *perceptiveSpeed;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadRegionRequestMetrics : QNUploadMetrics
|
||||
|
||||
@property (nonatomic, strong, readonly) NSNumber *requestCount;
|
||||
@property (nonatomic, strong, readonly) NSNumber *bytesSend;
|
||||
@property (nonatomic, strong, readonly) id <QNUploadRegion> region;
|
||||
@property (nonatomic, strong, readonly) QNUploadSingleRequestMetrics *lastMetrics;
|
||||
@property (nonatomic, copy, readonly) NSArray<QNUploadSingleRequestMetrics *> *metricsList;
|
||||
|
||||
//MARK:-- 构造
|
||||
- (instancetype)initWithRegion:(id <QNUploadRegion>)region;
|
||||
|
||||
- (void)addMetricsList:(NSArray <QNUploadSingleRequestMetrics *> *)metricsList;
|
||||
- (void)addMetrics:(QNUploadRegionRequestMetrics*)metrics;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadTaskMetrics : QNUploadMetrics
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *upType;
|
||||
@property (nonatomic, strong, readonly) NSNumber *requestCount;
|
||||
@property (nonatomic, strong, readonly) NSNumber *bytesSend;
|
||||
@property (nonatomic, strong, readonly) NSNumber *regionCount;
|
||||
@property (nonatomic, strong, readonly) QNUploadRegionRequestMetrics *lastMetrics;
|
||||
|
||||
@property (nonatomic, strong) QNUploadRegionRequestMetrics *ucQueryMetrics;
|
||||
@property (nonatomic, strong) NSArray<id <QNUploadRegion>> *regions;
|
||||
|
||||
+ (instancetype)taskMetrics:(NSString *)upType;
|
||||
|
||||
- (void)addMetrics:(QNUploadRegionRequestMetrics *)metrics;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
351
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestMetrics.m
generated
Normal file
351
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestMetrics.m
generated
Normal file
@@ -0,0 +1,351 @@
|
||||
//
|
||||
// QNUploadRequestMetrics.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUtils.h"
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
#import "QNZoneInfo.h"
|
||||
|
||||
@interface QNUploadMetrics()
|
||||
|
||||
@property (nullable, strong) NSDate *startDate;
|
||||
@property (nullable, strong) NSDate *endDate;
|
||||
|
||||
@end
|
||||
@implementation QNUploadMetrics
|
||||
//MARK:-- 构造
|
||||
+ (instancetype)emptyMetrics {
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalElapsedTime{
|
||||
return [QNUtils dateDuration:self.startDate endDate:self.endDate];
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
self.startDate = [NSDate date];
|
||||
}
|
||||
|
||||
- (void)end {
|
||||
self.endDate = [NSDate date];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface QNUploadSingleRequestMetrics()
|
||||
@property (nonatomic, assign) int64_t countOfRequestHeaderBytes;
|
||||
@property (nonatomic, assign) int64_t countOfRequestBodyBytes;
|
||||
@end
|
||||
@implementation QNUploadSingleRequestMetrics
|
||||
|
||||
+ (instancetype)emptyMetrics{
|
||||
QNUploadSingleRequestMetrics *metrics = [[QNUploadSingleRequestMetrics alloc] init];
|
||||
return metrics;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initData{
|
||||
_countOfRequestHeaderBytesSent = 0;
|
||||
_countOfRequestBodyBytesSent = 0;
|
||||
_countOfResponseHeaderBytesReceived = 0;
|
||||
_countOfResponseBodyBytesReceived = 0;
|
||||
}
|
||||
|
||||
- (void)setRequest:(NSURLRequest *)request{
|
||||
NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:request.URL
|
||||
cachePolicy:request.cachePolicy
|
||||
timeoutInterval:request.timeoutInterval];
|
||||
newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
|
||||
|
||||
self.countOfRequestHeaderBytes = [NSString stringWithFormat:@"%@", request.allHTTPHeaderFields].length;
|
||||
self.countOfRequestBodyBytes = [request.qn_getHttpBody length];
|
||||
_totalBytes = @(self.countOfRequestHeaderBytes + self.countOfRequestBodyBytes);
|
||||
_request = [newRequest copy];
|
||||
}
|
||||
|
||||
- (void)setResponse:(NSURLResponse *)response {
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]] &&
|
||||
[(NSHTTPURLResponse *)response statusCode] >= 200 &&
|
||||
[(NSHTTPURLResponse *)response statusCode] < 300) {
|
||||
_countOfRequestHeaderBytesSent = _countOfRequestHeaderBytes;
|
||||
_countOfRequestBodyBytesSent = _countOfRequestBodyBytes;
|
||||
}
|
||||
if (_countOfResponseBodyBytesReceived <= 0) {
|
||||
_countOfResponseBodyBytesReceived = response.expectedContentLength;
|
||||
}
|
||||
if (_countOfResponseHeaderBytesReceived <= 0 && [response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
_countOfResponseHeaderBytesReceived = [NSString stringWithFormat:@"%@", [(NSHTTPURLResponse *)response allHeaderFields]].length;
|
||||
}
|
||||
_response = [response copy];
|
||||
}
|
||||
|
||||
- (BOOL)isForsureHijacked {
|
||||
return [self.hijacked isEqualToString:kQNMetricsRequestHijacked];
|
||||
}
|
||||
|
||||
- (BOOL)isMaybeHijacked {
|
||||
return [self.hijacked isEqualToString:kQNMetricsRequestMaybeHijacked];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalElapsedTime{
|
||||
return [self timeFromStartDate:self.startDate
|
||||
toEndDate:self.endDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalDnsTime{
|
||||
return [self timeFromStartDate:self.domainLookupStartDate
|
||||
toEndDate:self.domainLookupEndDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalConnectTime{
|
||||
return [self timeFromStartDate:self.connectStartDate
|
||||
toEndDate:self.connectEndDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalSecureConnectTime{
|
||||
return [self timeFromStartDate:self.secureConnectionStartDate
|
||||
toEndDate:self.secureConnectionEndDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalRequestTime{
|
||||
return [self timeFromStartDate:self.requestStartDate
|
||||
toEndDate:self.requestEndDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalWaitTime{
|
||||
return [self timeFromStartDate:self.requestEndDate
|
||||
toEndDate:self.responseStartDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)totalResponseTime{
|
||||
return [self timeFromStartDate:self.responseStartDate
|
||||
toEndDate:self.responseEndDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)bytesSend{
|
||||
int64_t totalBytes = [self totalBytes].integerValue;
|
||||
int64_t senderBytes = self.countOfRequestBodyBytesSent + self.countOfRequestHeaderBytesSent;
|
||||
int64_t bytes = MIN(totalBytes, senderBytes);
|
||||
return @(bytes);
|
||||
}
|
||||
|
||||
- (NSNumber *)timeFromStartDate:(NSDate *)startDate toEndDate:(NSDate *)endDate{
|
||||
return [QNUtils dateDuration:startDate endDate:endDate];
|
||||
}
|
||||
|
||||
- (NSNumber *)perceptiveSpeed {
|
||||
int64_t size = self.bytesSend.longLongValue + _countOfResponseHeaderBytesReceived + _countOfResponseBodyBytesReceived;
|
||||
if (size == 0 || self.totalElapsedTime == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [QNUtils calculateSpeed:size totalTime:self.totalElapsedTime.longLongValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadRegionRequestMetrics()
|
||||
|
||||
@property (nonatomic, strong) id <QNUploadRegion> region;
|
||||
@property (nonatomic, copy) NSMutableArray<QNUploadSingleRequestMetrics *> *metricsListInter;
|
||||
|
||||
@end
|
||||
@implementation QNUploadRegionRequestMetrics
|
||||
|
||||
+ (instancetype)emptyMetrics{
|
||||
QNUploadRegionRequestMetrics *metrics = [[QNUploadRegionRequestMetrics alloc] init];
|
||||
return metrics;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRegion:(id<QNUploadRegion>)region{
|
||||
if (self = [super init]) {
|
||||
_region = region;
|
||||
_metricsListInter = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (QNUploadSingleRequestMetrics *)lastMetrics {
|
||||
@synchronized (self) {
|
||||
return self.metricsListInter.lastObject;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber *)requestCount{
|
||||
if (self.metricsList) {
|
||||
return @(self.metricsList.count);
|
||||
} else {
|
||||
return @(0);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber *)bytesSend{
|
||||
if (self.metricsList) {
|
||||
long long bytes = 0;
|
||||
for (QNUploadSingleRequestMetrics *metrics in self.metricsList) {
|
||||
bytes += metrics.bytesSend.longLongValue;
|
||||
}
|
||||
return @(bytes);
|
||||
} else {
|
||||
return @(0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addMetricsList:(NSArray<QNUploadSingleRequestMetrics *> *)metricsList{
|
||||
@synchronized (self) {
|
||||
[_metricsListInter addObjectsFromArray:metricsList];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addMetrics:(QNUploadRegionRequestMetrics*)metrics{
|
||||
if ([metrics.region.zoneInfo.regionId isEqualToString:self.region.zoneInfo.regionId]) {
|
||||
@synchronized (self) {
|
||||
[_metricsListInter addObjectsFromArray:metrics.metricsListInter];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<QNUploadSingleRequestMetrics *> *)metricsList{
|
||||
@synchronized (self) {
|
||||
return [_metricsListInter copy];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadTaskMetrics()
|
||||
|
||||
@property (nonatomic, copy) NSString *upType;
|
||||
@property (nonatomic, copy) NSMutableArray<NSString *> *metricsKeys;
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, QNUploadRegionRequestMetrics *> *metricsInfo;
|
||||
|
||||
@end
|
||||
@implementation QNUploadTaskMetrics
|
||||
|
||||
+ (instancetype)emptyMetrics{
|
||||
QNUploadTaskMetrics *metrics = [[QNUploadTaskMetrics alloc] init];
|
||||
return metrics;
|
||||
}
|
||||
|
||||
+ (instancetype)taskMetrics:(NSString *)upType {
|
||||
QNUploadTaskMetrics *metrics = [self emptyMetrics];
|
||||
metrics.upType = upType;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_metricsKeys = [NSMutableArray array];
|
||||
_metricsInfo = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (QNUploadRegionRequestMetrics *)lastMetrics {
|
||||
if (self.metricsKeys.count < 1) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
NSString *key = self.metricsKeys.lastObject;
|
||||
if (key == nil) {
|
||||
return nil;
|
||||
}
|
||||
return self.metricsInfo[key];
|
||||
}
|
||||
}
|
||||
- (NSNumber *)totalElapsedTime{
|
||||
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
|
||||
if (metricsInfo) {
|
||||
double time = 0;
|
||||
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
|
||||
time += metrics.totalElapsedTime.doubleValue;
|
||||
}
|
||||
return time > 0 ? @(time) : nil;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber *)requestCount{
|
||||
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
|
||||
if (metricsInfo) {
|
||||
NSInteger count = 0;
|
||||
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
|
||||
count += metrics.requestCount.integerValue;
|
||||
}
|
||||
return @(count);
|
||||
} else {
|
||||
return @(0);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber *)bytesSend{
|
||||
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
|
||||
if (metricsInfo) {
|
||||
long long bytes = 0;
|
||||
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
|
||||
bytes += metrics.bytesSend.longLongValue;
|
||||
}
|
||||
return @(bytes);
|
||||
} else {
|
||||
return @(0);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber *)regionCount{
|
||||
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
|
||||
if (metricsInfo) {
|
||||
int count = 0;
|
||||
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
|
||||
if (![metrics.region.zoneInfo.regionId isEqualToString:QNZoneInfoEmptyRegionId]) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return @(count);
|
||||
} else {
|
||||
return @(0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUcQueryMetrics:(QNUploadRegionRequestMetrics *)ucQueryMetrics {
|
||||
_ucQueryMetrics = ucQueryMetrics;
|
||||
[self addMetrics:ucQueryMetrics];
|
||||
}
|
||||
|
||||
- (void)addMetrics:(QNUploadRegionRequestMetrics *)metrics{
|
||||
NSString *regionId = metrics.region.zoneInfo.regionId;
|
||||
if (!regionId) {
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
QNUploadRegionRequestMetrics *metricsOld = self.metricsInfo[regionId];
|
||||
if (metricsOld) {
|
||||
[metricsOld addMetrics:metrics];
|
||||
} else {
|
||||
[self.metricsKeys addObject:regionId];
|
||||
self.metricsInfo[regionId] = metrics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, QNUploadRegionRequestMetrics *> *)syncCopyMetricsInfo {
|
||||
@synchronized (self) {
|
||||
return [_metricsInfo copy];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
25
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestState.h
generated
Normal file
25
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestState.h
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// QNUploadRequestState.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadRequestState : NSObject
|
||||
|
||||
// old server 不验证tls sni
|
||||
@property(nonatomic, assign)BOOL isUseOldServer;
|
||||
|
||||
// 用户是否取消
|
||||
@property(nonatomic, assign)BOOL isUserCancel;
|
||||
|
||||
- (instancetype)copy;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
32
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestState.m
generated
Normal file
32
Pods/Qiniu/QiniuSDK/Http/QNUploadRequestState.m
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// QNUploadRequestState.m
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/11/17.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNIUploadServer.h"
|
||||
#import "QNUploadRequestState.h"
|
||||
|
||||
@implementation QNUploadRequestState
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initData{
|
||||
_isUserCancel = NO;
|
||||
_isUseOldServer = NO;
|
||||
}
|
||||
|
||||
- (instancetype)copy {
|
||||
QNUploadRequestState *state = [[QNUploadRequestState alloc] init];
|
||||
state.isUserCancel = self.isUserCancel;
|
||||
state.isUseOldServer = self.isUseOldServer;
|
||||
return state;
|
||||
}
|
||||
|
||||
@end
|
||||
39
Pods/Qiniu/QiniuSDK/Http/QNUserAgent.h
generated
Normal file
39
Pods/Qiniu/QiniuSDK/Http/QNUserAgent.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// QNUserAgent.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14-9-29.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* UserAgent
|
||||
*
|
||||
*/
|
||||
|
||||
#define kQNUserAgent [QNUserAgent sharedInstance]
|
||||
@interface QNUserAgent : NSObject
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *id;
|
||||
|
||||
/**
|
||||
* UserAgent 字串
|
||||
*/
|
||||
- (NSString *)description;
|
||||
|
||||
/**
|
||||
* UserAgent + AK 字串
|
||||
* @param access access信息
|
||||
*/
|
||||
- (NSString *)getUserAgent:(NSString *)access;
|
||||
|
||||
/**
|
||||
* 单例
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
@end
|
||||
91
Pods/Qiniu/QiniuSDK/Http/QNUserAgent.m
generated
Normal file
91
Pods/Qiniu/QiniuSDK/Http/QNUserAgent.m
generated
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// QNUserAgent.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14-9-29.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#import "QNUserAgent.h"
|
||||
#import "QNUtils.h"
|
||||
|
||||
static NSString *qn_clientId(void) {
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
NSString *s = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
|
||||
if (s == nil) {
|
||||
s = @"simulator";
|
||||
}
|
||||
return s;
|
||||
#else
|
||||
long long now_timestamp = [[NSDate date] timeIntervalSince1970] * 1000;
|
||||
int r = arc4random() % 1000;
|
||||
return [NSString stringWithFormat:@"%lld%u", now_timestamp, r];
|
||||
#endif
|
||||
}
|
||||
|
||||
static NSString *qn_userAgent(NSString *id, NSString *ak) {
|
||||
NSString *addition = @"";
|
||||
#if DEBUG
|
||||
addition = @"_Debug";
|
||||
#endif
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
return [NSString stringWithFormat:@"QiniuObject-C%@/%@ (%@; iOS %@; %@; %@)", addition, [QNUtils sdkVersion], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], id, ak];
|
||||
#else
|
||||
return [NSString stringWithFormat:@"QiniuObject-C%@/%@ (Mac OS X %@; %@; %@)", addition, [QNUtils sdkVersion], [[NSProcessInfo processInfo] operatingSystemVersionString], id, ak];
|
||||
#endif
|
||||
}
|
||||
|
||||
@interface QNUserAgent ()
|
||||
@property (nonatomic) NSString *ua;
|
||||
@end
|
||||
|
||||
@implementation QNUserAgent
|
||||
|
||||
- (NSString *)description {
|
||||
return _ua;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_id = qn_clientId();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* UserAgent
|
||||
*/
|
||||
- (NSString *)getUserAgent:(NSString *)access {
|
||||
NSString *ak;
|
||||
if (access == nil || access.length == 0) {
|
||||
ak = @"-";
|
||||
} else {
|
||||
ak = access;
|
||||
}
|
||||
return qn_userAgent(_id, ak);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单例
|
||||
*/
|
||||
+ (instancetype)sharedInstance {
|
||||
static QNUserAgent *sharedInstance = nil;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
@end
|
||||
50
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/NSURLRequest+QNRequest.h
generated
Normal file
50
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/NSURLRequest+QNRequest.h
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// NSURLRequest+QNRequest.h
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/8.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSURLRequest(QNRequest)
|
||||
|
||||
/// 请求id【内部使用】
|
||||
/// 只有通过设置qn_domain才会有效
|
||||
@property(nonatomic, strong, nullable, readonly)NSString *qn_identifier;
|
||||
|
||||
/// 请求domain【内部使用】
|
||||
/// 只有通过NSMutableURLRequest设置才会有效
|
||||
@property(nonatomic, strong, nullable, readonly)NSString *qn_domain;
|
||||
|
||||
/// 请求ip【内部使用】
|
||||
/// 只有通过NSMutableURLRequest设置才会有效
|
||||
@property(nonatomic, strong, nullable, readonly)NSString *qn_ip;
|
||||
|
||||
/// 请求头信息 去除七牛内部标记占位
|
||||
@property(nonatomic, strong, nullable, readonly)NSDictionary *qn_allHTTPHeaderFields;
|
||||
|
||||
+ (instancetype)qn_requestWithURL:(NSURL *)url;
|
||||
|
||||
/// 获取请求体
|
||||
- (NSData *)qn_getHttpBody;
|
||||
|
||||
- (BOOL)qn_isHttps;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface NSMutableURLRequest(QNRequest)
|
||||
|
||||
/// 请求domain【内部使用】
|
||||
@property(nonatomic, strong, nullable)NSString *qn_domain;
|
||||
/// 请求ip【内部使用】
|
||||
@property(nonatomic, strong, nullable)NSString *qn_ip;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
123
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/NSURLRequest+QNRequest.m
generated
Normal file
123
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/NSURLRequest+QNRequest.m
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// NSURLRequest+QNRequest.m
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/8.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
|
||||
|
||||
@implementation NSURLRequest(QNRequest)
|
||||
|
||||
#define kQNURLRequestHostKey @"Host"
|
||||
#define kQNURLRequestIPKey @"QNURLRequestIP"
|
||||
#define kQNURLRequestIdentifierKey @"QNURLRequestIdentifier"
|
||||
|
||||
- (NSString *)qn_identifier{
|
||||
return self.allHTTPHeaderFields[kQNURLRequestIdentifierKey];
|
||||
}
|
||||
|
||||
- (NSString *)qn_domain{
|
||||
NSString *host = self.allHTTPHeaderFields[kQNURLRequestHostKey];
|
||||
if (host == nil) {
|
||||
host = self.URL.host;
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
- (NSString *)qn_ip{
|
||||
return self.allHTTPHeaderFields[kQNURLRequestIPKey];
|
||||
}
|
||||
|
||||
- (NSDictionary *)qn_allHTTPHeaderFields{
|
||||
NSDictionary *headerFields = [self.allHTTPHeaderFields copy];
|
||||
NSMutableDictionary *headerFieldsNew = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in headerFields) {
|
||||
if (![key isEqualToString:kQNURLRequestIdentifierKey]) {
|
||||
[headerFieldsNew setObject:headerFields[key] forKey:key];
|
||||
}
|
||||
}
|
||||
return [headerFieldsNew copy];
|
||||
}
|
||||
|
||||
+ (instancetype)qn_requestWithURL:(NSURL *)url{
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
[request setValue:url.host forHTTPHeaderField:kQNURLRequestHostKey];
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
- (NSData *)qn_getHttpBody{
|
||||
|
||||
if (self.HTTPBody ||
|
||||
(![self.HTTPMethod isEqualToString:@"POST"] && ![self.HTTPMethod isEqualToString:@"PUT"])) {
|
||||
return self.HTTPBody;
|
||||
}
|
||||
|
||||
NSInteger maxLength = 1024;
|
||||
uint8_t d[maxLength];
|
||||
|
||||
NSInputStream *stream = self.HTTPBodyStream;
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
|
||||
[stream open];
|
||||
|
||||
BOOL end = NO;
|
||||
|
||||
while (!end) {
|
||||
NSInteger bytesRead = [stream read:d maxLength:maxLength];
|
||||
if (bytesRead == 0) {
|
||||
end = YES;
|
||||
} else if (bytesRead == -1){
|
||||
end = YES;
|
||||
} else if (stream.streamError == nil){
|
||||
[data appendBytes:(void *)d length:bytesRead];
|
||||
}
|
||||
}
|
||||
[stream close];
|
||||
return [data copy];
|
||||
}
|
||||
|
||||
- (BOOL)qn_isHttps{
|
||||
if ([self.URL.absoluteString rangeOfString:@"https://"].location != NSNotFound) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSMutableURLRequest(QNRequest)
|
||||
|
||||
- (void)setQn_domain:(NSString *)qn_domain{
|
||||
if (qn_domain) {
|
||||
[self addValue:qn_domain forHTTPHeaderField:kQNURLRequestHostKey];
|
||||
} else {
|
||||
[self setValue:nil forHTTPHeaderField:kQNURLRequestHostKey];
|
||||
}
|
||||
|
||||
NSString *identifier = [NSString stringWithFormat:@"%p-%@", &self, qn_domain];
|
||||
[self setQn_identifier:identifier];
|
||||
}
|
||||
|
||||
- (void)setQn_ip:(NSString *)qn_ip{
|
||||
if (qn_ip) {
|
||||
[self addValue:qn_ip forHTTPHeaderField:kQNURLRequestIPKey];
|
||||
} else {
|
||||
[self setValue:nil forHTTPHeaderField:kQNURLRequestIPKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setQn_identifier:(NSString *)qn_identifier{
|
||||
if (qn_identifier) {
|
||||
[self addValue:qn_identifier forHTTPHeaderField:kQNURLRequestIdentifierKey];
|
||||
} else {
|
||||
[self setValue:nil forHTTPHeaderField:kQNURLRequestIdentifierKey];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClient.h
generated
Normal file
17
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClient.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// QNCFHttpClient.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/10/11.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNRequestClient.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNCFHttpClient : NSObject <QNRequestClient>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
160
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClient.m
generated
Normal file
160
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClient.m
generated
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// QNCFHttpClient.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/10/11.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNCFHttpClient.h"
|
||||
#import "QNCFHttpClientInner.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
#import "QNCFHttpThreadPool.h"
|
||||
|
||||
@interface QNCFHttpClient() <QNCFHttpClientInnerDelegate>
|
||||
|
||||
@property(nonatomic, assign)BOOL hasCallBack;
|
||||
@property(nonatomic, assign)NSInteger redirectCount;
|
||||
@property(nonatomic, assign)NSInteger maxRedirectCount;
|
||||
|
||||
@property(nonatomic, strong)NSURLRequest *request;
|
||||
@property(nonatomic, strong)NSURLResponse *response;
|
||||
@property(nonatomic, strong)NSDictionary *connectionProxy;
|
||||
@property(nonatomic, strong)QNUploadSingleRequestMetrics *requestMetrics;
|
||||
@property(nonatomic, strong)NSMutableData *responseData;
|
||||
@property(nonatomic, copy)void(^progress)(long long totalBytesWritten, long long totalBytesExpectedToWrite);
|
||||
@property(nonatomic, copy)QNRequestClientCompleteHandler complete;
|
||||
|
||||
@property(nonatomic, strong)QNCFHttpClientInner *httpClient;
|
||||
@property(nonatomic, strong)QNCFHttpThread *thread;
|
||||
|
||||
@end
|
||||
@implementation QNCFHttpClient
|
||||
|
||||
- (NSString *)clientId {
|
||||
return @"CFNetwork";
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.redirectCount = 0;
|
||||
self.maxRedirectCount = 15;
|
||||
self.hasCallBack = false;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)request:(NSURLRequest *)request
|
||||
server:(id <QNUploadServer>)server
|
||||
connectionProxy:(NSDictionary *)connectionProxy
|
||||
progress:(void (^)(long long, long long))progress
|
||||
complete:(QNRequestClientCompleteHandler)complete {
|
||||
|
||||
self.thread = [[QNCFHttpThreadPool shared] getOneThread];
|
||||
// 有 ip 才会使用
|
||||
if (server && server.ip.length > 0 && server.host.length > 0) {
|
||||
NSString *urlString = request.URL.absoluteString;
|
||||
urlString = [urlString stringByReplacingOccurrencesOfString:server.host withString:server.ip];
|
||||
NSMutableURLRequest *requestNew = [request mutableCopy];
|
||||
requestNew.URL = [NSURL URLWithString:urlString];
|
||||
requestNew.qn_domain = server.host;
|
||||
self.request = [requestNew copy];
|
||||
} else {
|
||||
self.request = request;
|
||||
}
|
||||
|
||||
self.connectionProxy = connectionProxy;
|
||||
self.progress = progress;
|
||||
self.complete = complete;
|
||||
self.requestMetrics = [QNUploadSingleRequestMetrics emptyMetrics];
|
||||
self.requestMetrics.request = self.request;
|
||||
self.requestMetrics.remoteAddress = self.request.qn_ip;
|
||||
self.requestMetrics.remotePort = self.request.qn_isHttps ? @443 : @80;
|
||||
[self.requestMetrics start];
|
||||
|
||||
self.responseData = [NSMutableData data];
|
||||
self.httpClient = [QNCFHttpClientInner client:self.request connectionProxy:connectionProxy];
|
||||
self.httpClient.delegate = self;
|
||||
[self.httpClient performSelector:@selector(main)
|
||||
onThread:self.thread
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
if (self.thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.httpClient performSelector:@selector(cancel)
|
||||
onThread:self.thread
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)completeAction:(NSError *)error {
|
||||
@synchronized (self) {
|
||||
if (self.hasCallBack) {
|
||||
return;
|
||||
}
|
||||
self.hasCallBack = true;
|
||||
}
|
||||
self.requestMetrics.response = self.response;
|
||||
[self.requestMetrics end];
|
||||
if (self.complete) {
|
||||
self.complete(self.response, self.requestMetrics, self.responseData, error);
|
||||
}
|
||||
[[QNCFHttpThreadPool shared] subtractOperationCountOfThread:self.thread];
|
||||
}
|
||||
|
||||
//MARK: -- delegate
|
||||
- (void)didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
|
||||
self.requestMetrics.countOfRequestBodyBytesSent = totalBytesSent;
|
||||
if (self.progress) {
|
||||
self.progress(totalBytesSent, totalBytesExpectedToSend);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didFinish {
|
||||
self.requestMetrics.responseEndDate = [NSDate date];
|
||||
[self completeAction:nil];
|
||||
}
|
||||
|
||||
- (void)didLoadData:(nonnull NSData *)data {
|
||||
[self.responseData appendData:data];
|
||||
}
|
||||
|
||||
- (void)onError:(nonnull NSError *)error {
|
||||
[self completeAction:error];
|
||||
}
|
||||
|
||||
- (void)onReceiveResponse:(NSURLResponse *)response httpVersion:(NSString *)httpVersion{
|
||||
self.requestMetrics.responseStartDate = [NSDate date];
|
||||
if ([httpVersion isEqualToString:@"http/1.0"]) {
|
||||
self.requestMetrics.httpVersion = @"1.0";
|
||||
} else if ([httpVersion isEqualToString:@"http/1.1"]) {
|
||||
self.requestMetrics.httpVersion = @"1.1";
|
||||
} else if ([httpVersion isEqualToString:@"h2"]) {
|
||||
self.requestMetrics.httpVersion = @"2";
|
||||
} else if ([httpVersion isEqualToString:@"h3"]) {
|
||||
self.requestMetrics.httpVersion = @"3";
|
||||
} else {
|
||||
self.requestMetrics.httpVersion = httpVersion;
|
||||
}
|
||||
self.response = response;
|
||||
}
|
||||
|
||||
- (void)redirectedToRequest:(nonnull NSURLRequest *)request redirectResponse:(nonnull NSURLResponse *)redirectResponse {
|
||||
if (self.redirectCount < self.maxRedirectCount) {
|
||||
self.redirectCount += 1;
|
||||
[self request:request server:nil connectionProxy:self.connectionProxy progress:self.progress complete:self.complete];
|
||||
} else {
|
||||
[self didFinish];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
43
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClientInner.h
generated
Normal file
43
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClientInner.h
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// QNHttpClient.h
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/7.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol QNCFHttpClientInnerDelegate <NSObject>
|
||||
|
||||
- (void)redirectedToRequest:(NSURLRequest *)request
|
||||
redirectResponse:(NSURLResponse *)redirectResponse;
|
||||
|
||||
- (void)onError:(NSError *)error;
|
||||
|
||||
- (void)didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
|
||||
|
||||
- (void)onReceiveResponse:(NSURLResponse *)response httpVersion:(NSString *)httpVersion;
|
||||
|
||||
- (void)didLoadData:(NSData *)data;
|
||||
|
||||
- (void)didFinish;
|
||||
|
||||
@end
|
||||
|
||||
@interface QNCFHttpClientInner : NSOperation
|
||||
|
||||
@property(nonatomic, strong, readonly)NSMutableURLRequest *request;
|
||||
@property(nonatomic, strong, readonly)NSDictionary *connectionProxy;
|
||||
|
||||
@property(nonatomic, weak)id <QNCFHttpClientInnerDelegate> delegate;
|
||||
|
||||
+ (instancetype)client:(NSURLRequest *)request connectionProxy:(NSDictionary *)connectionProxy;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
864
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClientInner.m
generated
Normal file
864
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpClientInner.m
generated
Normal file
@@ -0,0 +1,864 @@
|
||||
//
|
||||
// QNHttpClient.m
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/7.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNErrorCode.h"
|
||||
#import "QNDefine.h"
|
||||
#import "QNCFHttpClientInner.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
#import <sys/errno.h>
|
||||
|
||||
#define kQNCFHttpClientErrorDomain @"CFNetwork"
|
||||
|
||||
@interface QNCFHttpClientInner()<NSStreamDelegate>
|
||||
|
||||
@property(nonatomic, assign)BOOL isCallFinishOrError;
|
||||
@property(nonatomic, assign)BOOL isCompleted;
|
||||
@property(nonatomic, strong)NSMutableURLRequest *request;
|
||||
@property(nonatomic, strong)NSDictionary *connectionProxy;
|
||||
@property(nonatomic, assign)BOOL isReadResponseHeader;
|
||||
@property(nonatomic, assign)BOOL isReadResponseBody;
|
||||
@property(nonatomic, assign)BOOL isInputStreamEvaluated;
|
||||
@property(nonatomic, strong)NSInputStream *inputStream;
|
||||
|
||||
// 上传进度
|
||||
@property(nonatomic, strong)NSTimer *progressTimer; // 进度定时器
|
||||
@property(nonatomic, assign)int64_t totalBytesSent; // 已上传大小
|
||||
@property(nonatomic, assign)int64_t totalBytesExpectedToSend; // 总大小
|
||||
|
||||
@end
|
||||
@implementation QNCFHttpClientInner
|
||||
|
||||
+ (instancetype)client:(NSURLRequest *)request connectionProxy:(nonnull NSDictionary *)connectionProxy{
|
||||
if (!request) {
|
||||
return nil;
|
||||
}
|
||||
QNCFHttpClientInner *client = [[QNCFHttpClientInner alloc] init];
|
||||
client.connectionProxy = connectionProxy;
|
||||
client.request = [request mutableCopy];
|
||||
client.isCompleted = false;
|
||||
return client;
|
||||
}
|
||||
|
||||
- (void)main {
|
||||
[self prepare];
|
||||
[self openInputStream];
|
||||
[self startProgress];
|
||||
}
|
||||
|
||||
- (void)prepare {
|
||||
@autoreleasepool {
|
||||
self.inputStream = [self createInputStream:self.request];
|
||||
}
|
||||
|
||||
NSString *host = [self.request qn_domain];
|
||||
if ([self.request qn_isHttps]) {
|
||||
[self setInputStreamSNI:self.inputStream sni:host];
|
||||
}
|
||||
|
||||
[self setupProgress];
|
||||
}
|
||||
|
||||
- (void)releaseResource{
|
||||
[self endProgress:YES];
|
||||
[self closeInputStream];
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self releaseResource];
|
||||
[self delegate_onError:[self createError:NSURLErrorCancelled errorDescription:@"user cancel"]];
|
||||
}
|
||||
|
||||
//MARK: -- request -> stream
|
||||
- (NSInputStream *)createInputStream:(NSURLRequest *)urlRequest{
|
||||
|
||||
CFReadStreamRef readStream = NULL;
|
||||
@autoreleasepool {
|
||||
CFStringRef urlString = (__bridge CFStringRef) [urlRequest.URL absoluteString];
|
||||
CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault,
|
||||
urlString,
|
||||
NULL);
|
||||
CFStringRef httpMethod = (__bridge CFStringRef) urlRequest.HTTPMethod;
|
||||
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
|
||||
httpMethod,
|
||||
url,
|
||||
kCFHTTPVersion1_1);
|
||||
CFRelease(url);
|
||||
|
||||
NSDictionary *headFieldInfo = self.request.qn_allHTTPHeaderFields;
|
||||
for (NSString *headerField in headFieldInfo) {
|
||||
CFStringRef headerFieldP = (__bridge CFStringRef)headerField;
|
||||
CFStringRef headerFieldValueP = (__bridge CFStringRef)(headFieldInfo[headerField]);
|
||||
CFHTTPMessageSetHeaderFieldValue(request, headerFieldP, headerFieldValueP);
|
||||
}
|
||||
|
||||
NSData *httpBody = [self.request qn_getHttpBody];
|
||||
if (httpBody) {
|
||||
CFDataRef bodyData = (__bridge CFDataRef) httpBody;
|
||||
CFHTTPMessageSetBody(request, bodyData);
|
||||
}
|
||||
|
||||
readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
|
||||
CFRelease(request);
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
if (self.connectionProxy) {
|
||||
for (NSString *key in self.connectionProxy.allKeys) {
|
||||
NSObject *value = self.connectionProxy[key];
|
||||
if (key.length > 0) {
|
||||
CFReadStreamSetProperty(readStream, (__bridge CFTypeRef _Null_unspecified)key, (__bridge CFTypeRef _Null_unspecified)(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (__bridge_transfer NSInputStream *) readStream;
|
||||
}
|
||||
|
||||
- (void)setInputStreamSNI:(NSInputStream *)inputStream sni:(NSString *)sni{
|
||||
if (!sni || sni.length == 0) {
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings setObject:NSStreamSocketSecurityLevelNegotiatedSSL
|
||||
forKey:NSStreamSocketSecurityLevelKey];
|
||||
[settings setObject:sni
|
||||
forKey:(NSString *)kCFStreamSSLPeerName];
|
||||
[inputStream setProperty:settings forKey:(NSString *)CFBridgingRelease(kCFStreamPropertySSLSettings)];
|
||||
}
|
||||
|
||||
|
||||
//MARK: -- stream action
|
||||
- (void)openInputStream{
|
||||
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
self.inputStream.delegate = self;
|
||||
[self.inputStream open];
|
||||
}
|
||||
|
||||
- (void)closeInputStream {
|
||||
@synchronized (self) {
|
||||
if (self.inputStream) {
|
||||
[self.inputStream close];
|
||||
[self.inputStream setDelegate:nil];
|
||||
[self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
self.inputStream = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldEvaluateInputStreamServerTrust{
|
||||
if (![self.request qn_isHttps] || self.isInputStreamEvaluated) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)inputStreamGetAndNotifyHttpResponse{
|
||||
@synchronized (self) {
|
||||
if (self.isReadResponseHeader) {
|
||||
return;
|
||||
}
|
||||
self.isReadResponseHeader = YES;
|
||||
}
|
||||
|
||||
|
||||
CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
|
||||
CFHTTPMessageRef httpMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
|
||||
|
||||
CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(httpMessage);
|
||||
NSDictionary *headInfo = (__bridge_transfer NSDictionary *)headerFields;
|
||||
|
||||
CFStringRef httpVersion = CFHTTPMessageCopyVersion(httpMessage);
|
||||
NSString *httpVersionInfo = (__bridge_transfer NSString *)httpVersion;
|
||||
|
||||
CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(httpMessage);
|
||||
|
||||
if (![self isHttpRedirectStatusCode:statusCode]) {
|
||||
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL statusCode:statusCode HTTPVersion:httpVersionInfo headerFields:headInfo];
|
||||
[self delegate_onReceiveResponse:response httpVersion:httpVersionInfo];
|
||||
}
|
||||
|
||||
CFRelease(httpMessage);
|
||||
}
|
||||
|
||||
- (void)inputStreamGetAndNotifyHttpData{
|
||||
@synchronized (self) {
|
||||
if (self.isReadResponseBody) {
|
||||
return;
|
||||
}
|
||||
self.isReadResponseBody = YES;
|
||||
}
|
||||
|
||||
UInt8 buffer[16 * 1024];
|
||||
UInt8 *buf = NULL;
|
||||
NSUInteger length = 0;
|
||||
|
||||
if (![self.inputStream getBuffer:&buf length:&length]) {
|
||||
NSInteger amount = [self.inputStream read:buffer maxLength:sizeof(buffer)];
|
||||
buf = buffer;
|
||||
length = amount;
|
||||
}
|
||||
|
||||
NSData *data = [[NSData alloc] initWithBytes:buf length:length];
|
||||
[self delegate_didLoadData:data];
|
||||
}
|
||||
|
||||
- (BOOL)isInputStreamHttpResponseHeaderComplete{
|
||||
CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
|
||||
CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
|
||||
BOOL isComplete = CFHTTPMessageIsHeaderComplete(responseMessage);
|
||||
CFRelease(responseMessage);
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
- (BOOL)shouldInputStreamRedirect{
|
||||
CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
|
||||
CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
|
||||
CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage);
|
||||
CFRelease(responseMessage);
|
||||
return [self isHttpRedirectStatusCode:statusCode];
|
||||
}
|
||||
|
||||
- (BOOL)isHttpRedirectStatusCode:(NSInteger)code{
|
||||
if (code == 301 || code == 302 || code == 303 || code == 307) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)inputStreamRedirect{
|
||||
CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
|
||||
CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
|
||||
|
||||
CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(responseMessage);
|
||||
NSDictionary *headInfo = (__bridge_transfer NSDictionary *)headerFields;
|
||||
|
||||
NSString *urlString = headInfo[@"Location"];
|
||||
if (!urlString) {
|
||||
urlString = headInfo[@"location"];
|
||||
}
|
||||
if (!urlString) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFStringRef httpVersion = CFHTTPMessageCopyVersion(responseMessage);
|
||||
NSString *httpVersionString = (__bridge_transfer NSString *)httpVersion;
|
||||
|
||||
CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage);
|
||||
|
||||
NSDictionary *requestHeader = self.request.allHTTPHeaderFields;
|
||||
if (statusCode == 303) {
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
if (requestHeader[@"User-Agent"]) {
|
||||
header[@"User-Agent"] = requestHeader[@"User-Agent"];
|
||||
}
|
||||
if (requestHeader[@"Accept"]) {
|
||||
header[@"Accept"] = requestHeader[@"Accept"];
|
||||
}
|
||||
requestHeader = [header copy];
|
||||
}
|
||||
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = @"GET";
|
||||
[request setAllHTTPHeaderFields:requestHeader];
|
||||
NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
|
||||
statusCode:statusCode
|
||||
HTTPVersion:httpVersionString
|
||||
headerFields:headInfo];
|
||||
|
||||
[self releaseResource];
|
||||
[self delegate_redirectedToRequest:request redirectResponse:response];
|
||||
|
||||
CFRelease(responseMessage);
|
||||
}
|
||||
|
||||
//MARK: -- NSStreamDelegate
|
||||
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
|
||||
@autoreleasepool {
|
||||
switch (eventCode) {
|
||||
case NSStreamEventHasBytesAvailable:{
|
||||
if (![self isInputStreamHttpResponseHeaderComplete]) {
|
||||
break;
|
||||
}
|
||||
|
||||
[self inputStreamGetAndNotifyHttpResponse];
|
||||
[self inputStreamGetAndNotifyHttpData];
|
||||
}
|
||||
break;
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
break;
|
||||
case NSStreamEventErrorOccurred:{
|
||||
[self releaseResource];
|
||||
[self endProgress: YES];
|
||||
[self delegate_onError:[self translateCFNetworkErrorIntoUrlError:[aStream streamError]]];
|
||||
}
|
||||
break;
|
||||
case NSStreamEventEndEncountered:{
|
||||
if ([self shouldInputStreamRedirect]) {
|
||||
[self inputStreamRedirect];
|
||||
} else {
|
||||
|
||||
[self inputStreamGetAndNotifyHttpResponse];
|
||||
[self inputStreamGetAndNotifyHttpData];
|
||||
|
||||
[self releaseResource];
|
||||
[self endProgress: NO];
|
||||
[self delegate_didFinish];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -- progress and timer action
|
||||
- (void)setupProgress{
|
||||
self.totalBytesExpectedToSend = [self.request.qn_getHttpBody length];
|
||||
}
|
||||
|
||||
- (void)startProgress{
|
||||
[self createTimer];
|
||||
}
|
||||
|
||||
- (void)endProgress:(BOOL)hasError{
|
||||
|
||||
[self invalidateTimer];
|
||||
|
||||
if (!hasError) {
|
||||
[self delegate_didSendBodyData:self.totalBytesExpectedToSend - self.totalBytesSent
|
||||
totalBytesSent:self.totalBytesExpectedToSend
|
||||
totalBytesExpectedToSend:self.totalBytesExpectedToSend];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createTimer{
|
||||
|
||||
if (_progressTimer) {
|
||||
[self invalidateTimer];
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
|
||||
target:weak_self
|
||||
selector:@selector(timerAction)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
|
||||
|
||||
[self timerAction];
|
||||
_progressTimer = timer;
|
||||
}
|
||||
|
||||
- (void)invalidateTimer{
|
||||
[self.progressTimer invalidate];
|
||||
self.progressTimer = nil;
|
||||
}
|
||||
|
||||
- (void)timerAction{
|
||||
long long totalBytesSent = [(NSNumber *)CFBridgingRelease(CFReadStreamCopyProperty((CFReadStreamRef)[self inputStream], kCFStreamPropertyHTTPRequestBytesWrittenCount)) longLongValue];
|
||||
long long bytesSent = totalBytesSent - self.totalBytesSent;
|
||||
self.totalBytesSent = totalBytesSent;
|
||||
if (bytesSent > 0 && self.totalBytesSent <= self.totalBytesSent) {
|
||||
[self delegate_didSendBodyData:bytesSent
|
||||
totalBytesSent:self.totalBytesSent
|
||||
totalBytesExpectedToSend:self.totalBytesExpectedToSend];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSError *)translateCFNetworkErrorIntoUrlError:(NSError *)cfError{
|
||||
if (cfError == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInteger errorCode = kQNNetworkError;
|
||||
NSString *errorInfo = [NSString stringWithFormat:@"cf client:[%ld] %@", (long)cfError.code, cfError.localizedDescription];
|
||||
switch (cfError.code) {
|
||||
case ENOENT: /* No such file or directory */
|
||||
errorCode = NSFileNoSuchFileError;
|
||||
break;
|
||||
case EIO: /* Input/output error */
|
||||
errorCode = kQNLocalIOError;
|
||||
break;
|
||||
case E2BIG: /* Argument list too long */
|
||||
break;
|
||||
case ENOEXEC: /* Exec format error */
|
||||
errorCode = kQNLocalIOError;
|
||||
break;
|
||||
case EBADF: /* Bad file descriptor */
|
||||
errorCode = kQNLocalIOError;
|
||||
break;
|
||||
case ECHILD: /* No child processes */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EDEADLK: /* Resource deadlock avoided */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENOMEM: /* Cannot allocate memory */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EACCES: /* Permission denied */
|
||||
errorCode = NSURLErrorNoPermissionsToReadFile;
|
||||
break;
|
||||
case EFAULT: /* Bad address */
|
||||
errorCode = NSURLErrorBadURL;
|
||||
break;
|
||||
case EBUSY: /* Device / Resource busy */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EEXIST: /* File exists */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENODEV: /* Operation not supported by device */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EISDIR: /* Is a directory */
|
||||
errorCode = NSURLErrorFileIsDirectory;
|
||||
break;
|
||||
case ENOTDIR: /* Not a directory */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EINVAL: /* Invalid argument */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENFILE: /* Too many open files in system */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EMFILE: /* Too many open files */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EFBIG: /* File too large */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENOSPC: /* No space left on device */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ESPIPE: /* Illegal seek */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EMLINK: /* Too many links */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EPIPE: /* Broken pipe */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EDOM: /* Numerical argument out of domain */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ERANGE: /* Result too large */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EAGAIN: /* Resource temporarily unavailable */
|
||||
break;
|
||||
case ENOTSOCK: /* Socket operation on non-socket */
|
||||
break;
|
||||
case EDESTADDRREQ: /* Destination address required */
|
||||
errorCode = NSURLErrorBadURL;
|
||||
break;
|
||||
case EMSGSIZE: /* Message too long */
|
||||
break;
|
||||
case EPROTOTYPE: /* Protocol wrong type for socket */
|
||||
break;
|
||||
case ENOPROTOOPT: /* Protocol not available */
|
||||
break;
|
||||
case EPROTONOSUPPORT: /* Protocol not supported */
|
||||
break;
|
||||
case ENOTSUP: /* Operation not supported */
|
||||
break;
|
||||
case EPFNOSUPPORT: /* Protocol family not supported */
|
||||
break;
|
||||
case EAFNOSUPPORT: /* Address family not supported by protocol family */
|
||||
break;
|
||||
case EADDRINUSE: /* Address already in use */
|
||||
break;
|
||||
case EADDRNOTAVAIL: /* Can't assign requested address */
|
||||
break;
|
||||
case ENETDOWN: /* Network is down */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case ENETUNREACH: /* Network is unreachable */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case ENETRESET: /* Network dropped connection on reset */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case ECONNABORTED: /* Software caused connection abort */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case ECONNRESET: /* Connection reset by peer */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case ENOBUFS: /* No buffer space available */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EISCONN: /* Socket is already connected */
|
||||
break;
|
||||
case ENOTCONN: /* Socket is not connected */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case ESHUTDOWN: /* Can't send after socket shutdown */
|
||||
break;
|
||||
case ETOOMANYREFS: /* Too many references: can't splice */
|
||||
break;
|
||||
case ETIMEDOUT: /* Operation timed out */
|
||||
errorCode = NSURLErrorTimedOut;
|
||||
break;
|
||||
case ECONNREFUSED: /* Connection refused */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case ELOOP: /* Too many levels of symbolic links */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENAMETOOLONG: /* File name too long */
|
||||
break;
|
||||
case EHOSTDOWN: /* Host is down */
|
||||
break;
|
||||
case EHOSTUNREACH: /* No route to host */
|
||||
break;
|
||||
case ENOTEMPTY: /* Directory not empty */
|
||||
break;
|
||||
case EPROCLIM: /* Too many processes */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EUSERS: /* Too many users */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EDQUOT: /* Disc quota exceeded */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ESTALE: /* Stale NFS file handle */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EREMOTE: /* Too many levels of remote in path */
|
||||
break;
|
||||
case EBADRPC: /* RPC struct is bad */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ERPCMISMATCH: /* RPC version wrong */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EPROGUNAVAIL: /* RPC prog. not avail */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EPROGMISMATCH: /* Program version wrong */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EPROCUNAVAIL: /* Bad procedure for program */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENOLCK: /* No locks available */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ENOSYS: /* Function not implemented */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EFTYPE: /* Inappropriate file type or format */
|
||||
break;
|
||||
case EAUTH: /* Authentication error */
|
||||
break;
|
||||
case ENEEDAUTH: /* Need authenticator */
|
||||
break;
|
||||
case EPWROFF: /* Device power is off */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EDEVERR: /* Device error, e.g. paper out */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EOVERFLOW: /* Value too large to be stored in data type */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EBADEXEC: /* Bad executable */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EBADARCH: /* Bad CPU type in executable */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ESHLIBVERS: /* Shared library version mismatch */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EBADMACHO: /* Malformed Macho file */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case ECANCELED: /* Operation canceled */
|
||||
errorCode = NSURLErrorCancelled;
|
||||
break;
|
||||
case EIDRM: /* Identifier removed */
|
||||
break;
|
||||
case ENOMSG: /* No message of desired type */
|
||||
break;
|
||||
case EILSEQ: /* Illegal byte sequence */
|
||||
break;
|
||||
case ENOATTR: /* Attribute not found */
|
||||
break;
|
||||
case EBADMSG: /* Bad message */
|
||||
break;
|
||||
case EMULTIHOP: /* Reserved */
|
||||
break;
|
||||
case ENODATA: /* No message available on STREAM */
|
||||
break;
|
||||
case ENOLINK: /* Reserved */
|
||||
break;
|
||||
case ENOSR: /* No STREAM resources */
|
||||
break;
|
||||
case ENOSTR: /* Not a STREAM */
|
||||
break;
|
||||
case EPROTO: /* Protocol error */
|
||||
break;
|
||||
case ETIME: /* STREAM ioctl timeout */
|
||||
errorCode = NSURLErrorTimedOut;
|
||||
break;
|
||||
case EOPNOTSUPP: /* Operation not supported on socket */
|
||||
break;
|
||||
case ENOPOLICY: /* No such policy registered */
|
||||
break;
|
||||
case ENOTRECOVERABLE: /* State not recoverable */
|
||||
break;
|
||||
case EOWNERDEAD: /* Previous owner died */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case EQFULL: /* Interface output queue is full */
|
||||
break;
|
||||
case -9800: /* SSL protocol error */
|
||||
errorCode = NSURLErrorSecureConnectionFailed;
|
||||
break;
|
||||
case -9801: /* Cipher Suite negotiation failure */
|
||||
errorCode = NSURLErrorSecureConnectionFailed;
|
||||
break;
|
||||
case -9802: /* Fatal alert */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9803: /* I/O would block (not fatal) */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9804: /* attempt to restore an unknown session */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9805: /* connection closed gracefully */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case -9806: /* connection closed via error */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case -9807: /* invalid certificate chain */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9808: /* bad certificate format */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9809: /* underlying cryptographic error */
|
||||
errorCode = NSURLErrorSecureConnectionFailed;
|
||||
break;
|
||||
case -9810: /* Internal error */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9811: /* module attach failure */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9812: /* valid cert chain, untrusted root */
|
||||
errorCode = NSURLErrorServerCertificateHasUnknownRoot;
|
||||
break;
|
||||
case -9813: /* cert chain not verified by root */
|
||||
errorCode = NSURLErrorServerCertificateHasUnknownRoot;
|
||||
break;
|
||||
case -9814: /* chain had an expired cert */
|
||||
errorCode = NSURLErrorServerCertificateHasBadDate;
|
||||
break;
|
||||
case -9815: /* chain had a cert not yet valid */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9816: /* server closed session with no notification */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case -9817: /* insufficient buffer provided */
|
||||
errorCode = NSURLErrorCannotDecodeRawData;
|
||||
break;
|
||||
case -9818: /* bad SSLCipherSuite */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
case -9819: /* unexpected message received */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9820: /* bad MAC */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9821: /* decryption failed */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9822: /* record overflow */
|
||||
errorCode = NSURLErrorDataLengthExceedsMaximum;
|
||||
break;
|
||||
case -9823: /* decompression failure */
|
||||
errorCode = NSURLErrorDownloadDecodingFailedMidStream;
|
||||
break;
|
||||
case -9824: /* handshake failure */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
case -9825: /* misc. bad certificate */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9826: /* bad unsupported cert format */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9827: /* certificate revoked */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9828: /* certificate expired */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9829: /* unknown certificate */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9830: /* illegal parameter */
|
||||
errorCode = NSURLErrorCannotDecodeRawData;
|
||||
break;
|
||||
case -9831: /* unknown Cert Authority */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9832: /* access denied */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
case -9833: /* decoding error */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9834: /* decryption error */
|
||||
errorCode = NSURLErrorCannotDecodeRawData;
|
||||
break;
|
||||
case -9835: /* export restriction */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case -9836: /* bad protocol version */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case -9837: /* insufficient security */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
case -9838: /* internal error */
|
||||
errorCode = NSURLErrorTimedOut;
|
||||
break;
|
||||
case -9839: /* user canceled */
|
||||
errorCode = NSURLErrorCancelled;
|
||||
break;
|
||||
case -9840: /* no renegotiation allowed */
|
||||
errorCode = NSURLErrorCannotConnectToHost;
|
||||
break;
|
||||
case -9841: /* peer cert is valid, or was ignored if verification disabled */
|
||||
errorCode = NSURLErrorServerCertificateNotYetValid;
|
||||
break;
|
||||
case -9842: /* server has requested a client cert */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
case -9843: /* peer host name mismatch */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9844: /* peer dropped connection before responding */
|
||||
errorCode = NSURLErrorNetworkConnectionLost;
|
||||
break;
|
||||
case -9845: /* decryption failure */
|
||||
errorCode = NSURLErrorCannotDecodeRawData;
|
||||
break;
|
||||
case -9846: /* bad MAC */
|
||||
errorCode = NSURLErrorNotConnectedToInternet;
|
||||
break;
|
||||
case -9847: /* record overflow */
|
||||
errorCode = NSURLErrorDataLengthExceedsMaximum;
|
||||
break;
|
||||
case -9848: /* configuration error */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9849: /* unexpected (skipped) record in DTLS */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9850: /* weak ephemeral dh key */
|
||||
errorCode = kQNUnexpectedSysCallError;
|
||||
break;
|
||||
case -9851: /* SNI */
|
||||
errorCode = NSURLErrorClientCertificateRejected;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return [NSError errorWithDomain:NSURLErrorDomain code:errorCode userInfo:@{@"UserInfo" : errorInfo ?: @""}];
|
||||
}
|
||||
|
||||
|
||||
//MARK: -- delegate action
|
||||
- (void)delegate_redirectedToRequest:(NSURLRequest *)request
|
||||
redirectResponse:(NSURLResponse *)redirectResponse{
|
||||
if ([self.delegate respondsToSelector:@selector(redirectedToRequest:redirectResponse:)]) {
|
||||
[self.delegate redirectedToRequest:request redirectResponse:redirectResponse];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)delegate_onError:(NSError *)error{
|
||||
@synchronized (self) {
|
||||
if (self.isCallFinishOrError) {
|
||||
return;
|
||||
}
|
||||
self.isCallFinishOrError = YES;
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(onError:)]) {
|
||||
[self.delegate onError:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)delegate_didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
|
||||
if ([self.delegate respondsToSelector:@selector(didSendBodyData:
|
||||
totalBytesSent:
|
||||
totalBytesExpectedToSend:)]) {
|
||||
[self.delegate didSendBodyData:bytesSent
|
||||
totalBytesSent:totalBytesSent
|
||||
totalBytesExpectedToSend:totalBytesExpectedToSend];
|
||||
}
|
||||
}
|
||||
- (void)delegate_onReceiveResponse:(NSURLResponse *)response httpVersion:(NSString *)httpVersion{
|
||||
if ([self.delegate respondsToSelector:@selector(onReceiveResponse:httpVersion:)]) {
|
||||
[self.delegate onReceiveResponse:response httpVersion:httpVersion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)delegate_didLoadData:(NSData *)data{
|
||||
if ([self.delegate respondsToSelector:@selector(didLoadData:)]) {
|
||||
[self.delegate didLoadData:data];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)delegate_didFinish{
|
||||
@synchronized (self) {
|
||||
if (self.isCallFinishOrError) {
|
||||
return;
|
||||
}
|
||||
self.isCallFinishOrError = YES;
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(didFinish)]) {
|
||||
[self.delegate didFinish];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: error
|
||||
- (NSError *)createError:(NSInteger)errorCode errorDescription:(NSString *)errorDescription {
|
||||
if (errorDescription) {
|
||||
return [NSError errorWithDomain:kQNCFHttpClientErrorDomain
|
||||
code:errorCode
|
||||
userInfo:@{@"userInfo":errorDescription}];
|
||||
} else {
|
||||
return [NSError errorWithDomain:kQNCFHttpClientErrorDomain
|
||||
code:NSURLErrorSecureConnectionFailed
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
31
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpThreadPool.h
generated
Normal file
31
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpThreadPool.h
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// QNCFHttpThreadPool.h
|
||||
// Qiniu
|
||||
//
|
||||
// Created by yangsen on 2021/10/13.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNCFHttpThread : NSThread
|
||||
|
||||
@property(nonatomic, assign, readonly)NSInteger operationCount;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNCFHttpThreadPool : NSObject
|
||||
|
||||
@property(nonatomic, assign, readonly)NSInteger maxOperationPerThread;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (QNCFHttpThread *)getOneThread;
|
||||
- (void)addOperationCountOfThread:(QNCFHttpThread *)thread;
|
||||
- (void)subtractOperationCountOfThread:(QNCFHttpThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
128
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpThreadPool.m
generated
Normal file
128
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/CFNetwork/QNCFHttpThreadPool.m
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// QNCFHttpThreadPool.m
|
||||
// Qiniu
|
||||
//
|
||||
// Created by yangsen on 2021/10/13.
|
||||
//
|
||||
|
||||
#import "QNCFHttpThreadPool.h"
|
||||
#import "QNTransactionManager.h"
|
||||
|
||||
@interface QNCFHttpThread()
|
||||
@property(nonatomic, assign)BOOL isCompleted;
|
||||
@property(nonatomic, assign)NSInteger operationCount;
|
||||
@property(nonatomic, strong)NSDate *deadline;
|
||||
@end
|
||||
@implementation QNCFHttpThread
|
||||
+ (instancetype)thread {
|
||||
return [[QNCFHttpThread alloc] init];;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.isCompleted = NO;
|
||||
self.operationCount = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main {
|
||||
@autoreleasepool {
|
||||
[super main];
|
||||
|
||||
while (!self.isCompleted) {
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
self.isCompleted = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface QNCFHttpThreadPool()
|
||||
// 单位:秒
|
||||
@property(nonatomic, assign)NSInteger threadLiveTime;
|
||||
@property(nonatomic, assign)NSInteger maxOperationPerThread;
|
||||
@property(nonatomic, strong)NSMutableArray *pool;
|
||||
@end
|
||||
@implementation QNCFHttpThreadPool
|
||||
|
||||
+ (instancetype)shared {
|
||||
static QNCFHttpThreadPool *pool = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
pool = [[QNCFHttpThreadPool alloc] init];
|
||||
pool.threadLiveTime = 60;
|
||||
pool.maxOperationPerThread = 1;
|
||||
pool.pool = [NSMutableArray array];
|
||||
[pool addThreadLiveChecker];
|
||||
});
|
||||
return pool;
|
||||
}
|
||||
|
||||
- (void)addThreadLiveChecker {
|
||||
QNTransaction *transaction = [QNTransaction timeTransaction:@"CFHttpThreadPool" after:0 interval:1 action:^{
|
||||
[[QNCFHttpThreadPool shared] checkThreadLive];
|
||||
}];
|
||||
[kQNTransactionManager addTransaction:transaction];
|
||||
}
|
||||
|
||||
- (void)checkThreadLive {
|
||||
@synchronized (self) {
|
||||
NSArray *pool = [self.pool copy];
|
||||
for (QNCFHttpThread *thread in pool) {
|
||||
if (thread.operationCount < 1 && thread.deadline && [thread.deadline timeIntervalSinceNow] < 0) {
|
||||
[self.pool removeObject:thread];
|
||||
[thread cancel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (QNCFHttpThread *)getOneThread {
|
||||
QNCFHttpThread *thread = nil;
|
||||
@synchronized (self) {
|
||||
for (QNCFHttpThread *t in self.pool) {
|
||||
if (t.operationCount < self.maxOperationPerThread) {
|
||||
thread = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (thread == nil) {
|
||||
thread = [QNCFHttpThread thread];
|
||||
thread.name = [NSString stringWithFormat:@"com.qiniu.cfclient.%lu", (unsigned long)self.pool.count];
|
||||
[thread start];
|
||||
[self.pool addObject:thread];
|
||||
}
|
||||
thread.operationCount += 1;
|
||||
thread.deadline = nil;
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
- (void)addOperationCountOfThread:(QNCFHttpThread *)thread {
|
||||
if (thread == nil) {
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
thread.operationCount += 1;
|
||||
thread.deadline = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)subtractOperationCountOfThread:(QNCFHttpThread *)thread {
|
||||
if (thread == nil) {
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
thread.operationCount -= 1;
|
||||
if (thread.operationCount < 1) {
|
||||
thread.deadline = [NSDate dateWithTimeIntervalSinceNow:self.threadLiveTime];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/QNUploadSystemClient.h
generated
Normal file
17
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/QNUploadSystemClient.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// QNUploadSystemClient.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/6.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNRequestClient.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadSystemClient : NSObject <QNRequestClient>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
202
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/QNUploadSystemClient.m
generated
Normal file
202
Pods/Qiniu/QiniuSDK/Http/Request/HttpClient/QNUploadSystemClient.m
generated
Normal file
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// QNUploadSystemClient.m
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/6.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadSystemClient.h"
|
||||
#import "QNUserAgent.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
|
||||
@interface QNUploadSystemClient()<NSURLSessionDelegate>
|
||||
|
||||
@property(nonatomic, strong)NSURLRequest *request;
|
||||
@property(nonatomic, strong)QNUploadSingleRequestMetrics *requestMetrics;
|
||||
@property(nonatomic, strong)NSURLSessionDataTask *uploadTask;
|
||||
@property(nonatomic, strong)NSMutableData *responseData;
|
||||
@property(nonatomic, copy)void(^progress)(long long totalBytesWritten, long long totalBytesExpectedToWrite);
|
||||
@property(nonatomic, copy)QNRequestClientCompleteHandler complete;
|
||||
|
||||
@end
|
||||
@implementation QNUploadSystemClient
|
||||
|
||||
- (NSString *)clientId {
|
||||
return @"NSURLSession";
|
||||
}
|
||||
|
||||
- (void)request:(NSURLRequest *)request
|
||||
server:(id <QNUploadServer>)server
|
||||
connectionProxy:(NSDictionary *)connectionProxy
|
||||
progress:(void (^)(long long, long long))progress
|
||||
complete:(QNRequestClientCompleteHandler)complete {
|
||||
|
||||
// 非 https 方可使用 IP
|
||||
if (!request.qn_isHttps && server && server.ip.length > 0 && server.host.length > 0) {
|
||||
NSString *urlString = request.URL.absoluteString;
|
||||
urlString = [urlString stringByReplacingOccurrencesOfString:server.host withString:server.ip];
|
||||
NSMutableURLRequest *requestNew = [request mutableCopy];
|
||||
requestNew.URL = [NSURL URLWithString:urlString];
|
||||
requestNew.qn_domain = server.host;
|
||||
self.request = [requestNew copy];
|
||||
} else {
|
||||
self.request = request;
|
||||
}
|
||||
|
||||
self.requestMetrics = [QNUploadSingleRequestMetrics emptyMetrics];
|
||||
self.requestMetrics.remoteAddress = self.request.qn_isHttps ? nil : server.ip;
|
||||
self.requestMetrics.remotePort = self.request.qn_isHttps ? @443 : @80;
|
||||
[self.requestMetrics start];
|
||||
|
||||
self.responseData = [NSMutableData data];
|
||||
self.progress = progress;
|
||||
self.complete = complete;
|
||||
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
if (connectionProxy) {
|
||||
configuration.connectionProxyDictionary = connectionProxy;
|
||||
}
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:self.request];
|
||||
[uploadTask resume];
|
||||
|
||||
self.uploadTask = uploadTask;
|
||||
}
|
||||
|
||||
- (void)cancel{
|
||||
[self.uploadTask cancel];
|
||||
}
|
||||
|
||||
//MARK:-- NSURLSessionDelegate
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
|
||||
[self.responseData appendData:data];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
|
||||
[self.requestMetrics end];
|
||||
self.requestMetrics.request = task.currentRequest;
|
||||
self.requestMetrics.response = task.response;
|
||||
self.requestMetrics.error = error;
|
||||
self.complete(task.response, self.requestMetrics,self.responseData, error);
|
||||
|
||||
[session finishTasksAndInvalidate];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(ios(10.0)) {
|
||||
NSURLSessionTaskTransactionMetrics *transactionMetrics = metrics.transactionMetrics.lastObject;
|
||||
|
||||
self.requestMetrics.domainLookupStartDate = transactionMetrics.domainLookupStartDate;
|
||||
self.requestMetrics.domainLookupEndDate = transactionMetrics.domainLookupEndDate;
|
||||
self.requestMetrics.connectStartDate = transactionMetrics.connectStartDate;
|
||||
self.requestMetrics.secureConnectionStartDate = transactionMetrics.secureConnectionStartDate;
|
||||
self.requestMetrics.secureConnectionEndDate = transactionMetrics.secureConnectionEndDate;
|
||||
self.requestMetrics.connectEndDate = transactionMetrics.connectEndDate;
|
||||
|
||||
self.requestMetrics.requestStartDate = transactionMetrics.requestStartDate;
|
||||
self.requestMetrics.requestEndDate = transactionMetrics.requestEndDate;
|
||||
self.requestMetrics.responseStartDate = transactionMetrics.responseStartDate;
|
||||
self.requestMetrics.responseEndDate = transactionMetrics.responseEndDate;
|
||||
|
||||
if ([transactionMetrics.networkProtocolName isEqualToString:@"http/1.0"]) {
|
||||
self.requestMetrics.httpVersion = @"1.0";
|
||||
} else if ([transactionMetrics.networkProtocolName isEqualToString:@"http/1.1"]) {
|
||||
self.requestMetrics.httpVersion = @"1.1";
|
||||
} else if ([transactionMetrics.networkProtocolName isEqualToString:@"h2"]) {
|
||||
self.requestMetrics.httpVersion = @"2";
|
||||
} else if ([transactionMetrics.networkProtocolName isEqualToString:@"h3"]) {
|
||||
self.requestMetrics.httpVersion = @"3";
|
||||
} else {
|
||||
self.requestMetrics.httpVersion = transactionMetrics.networkProtocolName;
|
||||
}
|
||||
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
||||
if (@available(iOS 13.0, macOS 10.15, *)) {
|
||||
if (transactionMetrics.remoteAddress) {
|
||||
self.requestMetrics.remoteAddress = transactionMetrics.remoteAddress;
|
||||
self.requestMetrics.remotePort = transactionMetrics.remotePort;
|
||||
}
|
||||
if (transactionMetrics.countOfRequestHeaderBytesSent > 0) {
|
||||
self.requestMetrics.countOfRequestHeaderBytesSent = transactionMetrics.countOfRequestHeaderBytesSent;
|
||||
}
|
||||
if (transactionMetrics.countOfResponseHeaderBytesReceived > 0) {
|
||||
self.requestMetrics.countOfResponseHeaderBytesReceived = transactionMetrics.countOfResponseHeaderBytesReceived;
|
||||
}
|
||||
if (transactionMetrics.countOfResponseBodyBytesReceived > 0) {
|
||||
self.requestMetrics.countOfResponseBodyBytesReceived = transactionMetrics.countOfResponseBodyBytesReceived;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
||||
didSendBodyData:(int64_t)bytesSent
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
||||
|
||||
self.requestMetrics.countOfRequestBodyBytesSent = totalBytesSent;
|
||||
if (self.progress) {
|
||||
self.progress(totalBytesSent, totalBytesExpectedToSend);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
|
||||
forDomain:(NSString *)domain {
|
||||
|
||||
NSMutableArray *policies = [NSMutableArray array];
|
||||
if (domain) {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
|
||||
} else {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
|
||||
}
|
||||
|
||||
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
|
||||
|
||||
if (@available(iOS 13.0, macOS 10.14, *)) {
|
||||
CFErrorRef error = NULL;
|
||||
BOOL ret = SecTrustEvaluateWithError(serverTrust, &error);
|
||||
return ret && (error == nil);
|
||||
} else {
|
||||
SecTrustResultType result;
|
||||
SecTrustEvaluate(serverTrust, &result);
|
||||
return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
|
||||
if (!challenge) {
|
||||
return;
|
||||
}
|
||||
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
NSURLCredential *credential = nil;
|
||||
|
||||
NSString* host = [[self.request allHTTPHeaderFields] objectForKey:@"host"];
|
||||
if (!host) {
|
||||
host = self.request.URL.host;
|
||||
}
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
|
||||
completionHandler(disposition,credential);
|
||||
}
|
||||
*/
|
||||
@end
|
||||
56
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpRegionRequest.h
generated
Normal file
56
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpRegionRequest.h
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// QNHttpRequest.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNHttpSingleRequest.h"
|
||||
#import "QNUploadRegionInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@class QNUploadRequestState, QNResponseInfo, QNConfiguration, QNUploadOption, QNUpToken, QNUploadRegionRequestMetrics;
|
||||
|
||||
typedef void(^QNRegionRequestCompleteHandler)(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response);
|
||||
|
||||
@interface QNHttpRegionRequest : NSObject
|
||||
|
||||
@property(nonatomic, strong, readonly)QNConfiguration *config;
|
||||
@property(nonatomic, strong, readonly)QNUploadOption *uploadOption;
|
||||
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
token:(QNUpToken *)token
|
||||
region:(id <QNUploadRegion>)region
|
||||
requestInfo:(QNUploadRequestInfo *)requestInfo
|
||||
requestState:(QNUploadRequestState *)requestState;
|
||||
|
||||
|
||||
- (void)get:(NSString * _Nullable)action
|
||||
headers:(NSDictionary * _Nullable)headers
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))shouldRetry
|
||||
complete:(QNRegionRequestCompleteHandler)complete;
|
||||
|
||||
- (void)post:(NSString * _Nullable)action
|
||||
headers:(NSDictionary * _Nullable)headers
|
||||
body:(NSData * _Nullable)body
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))shouldRetry
|
||||
progress:(void(^_Nullable)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRegionRequestCompleteHandler)complete;
|
||||
|
||||
|
||||
- (void)put:(NSString *)action
|
||||
headers:(NSDictionary * _Nullable)headers
|
||||
body:(NSData * _Nullable)body
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRegionRequestCompleteHandler)complete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
216
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpRegionRequest.m
generated
Normal file
216
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpRegionRequest.m
generated
Normal file
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// QNHttpRequest.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNDnsPrefetch.h"
|
||||
#import "QNUploadRequestState.h"
|
||||
#import "QNHttpRegionRequest.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNUrlUtils.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
#import "QNResponseInfo.h"
|
||||
|
||||
@interface QNHttpRegionRequest()
|
||||
|
||||
@property(nonatomic, strong)QNConfiguration *config;
|
||||
@property(nonatomic, strong)QNUploadOption *uploadOption;
|
||||
@property(nonatomic, strong)QNUploadRequestInfo *requestInfo;
|
||||
@property(nonatomic, strong)QNUploadRequestState *requestState;
|
||||
|
||||
@property(nonatomic, strong)QNUploadRegionRequestMetrics *requestMetrics;
|
||||
@property(nonatomic, strong)QNHttpSingleRequest *singleRequest;
|
||||
|
||||
@property(nonatomic, strong)id <QNUploadServer> currentServer;
|
||||
@property(nonatomic, strong)id <QNUploadRegion> region;
|
||||
|
||||
@end
|
||||
@implementation QNHttpRegionRequest
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
token:(QNUpToken *)token
|
||||
region:(id <QNUploadRegion>)region
|
||||
requestInfo:(QNUploadRequestInfo *)requestInfo
|
||||
requestState:(QNUploadRequestState *)requestState {
|
||||
if (self = [super init]) {
|
||||
_config = config;
|
||||
_uploadOption = uploadOption;
|
||||
_region = region;
|
||||
_requestInfo = requestInfo;
|
||||
_requestState = requestState;
|
||||
_singleRequest = [[QNHttpSingleRequest alloc] initWithConfig:config
|
||||
uploadOption:uploadOption
|
||||
token:token
|
||||
requestInfo:requestInfo
|
||||
requestState:requestState];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)get:(NSString *)action
|
||||
headers:(NSDictionary *)headers
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
complete:(QNRegionRequestCompleteHandler)complete{
|
||||
|
||||
self.requestMetrics = [[QNUploadRegionRequestMetrics alloc] initWithRegion:self.region];
|
||||
[self.requestMetrics start];
|
||||
[self performRequest:[self getNextServer:nil]
|
||||
action:action
|
||||
headers:headers
|
||||
method:@"GET"
|
||||
body:nil
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
- (void)post:(NSString *)action
|
||||
headers:(NSDictionary *)headers
|
||||
body:(NSData *)body
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRegionRequestCompleteHandler)complete{
|
||||
|
||||
self.requestMetrics = [[QNUploadRegionRequestMetrics alloc] initWithRegion:self.region];
|
||||
[self.requestMetrics start];
|
||||
[self performRequest:[self getNextServer:nil]
|
||||
action:action
|
||||
headers:headers
|
||||
method:@"POST"
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
|
||||
- (void)put:(NSString *)action
|
||||
headers:(NSDictionary *)headers
|
||||
body:(NSData *)body
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRegionRequestCompleteHandler)complete{
|
||||
|
||||
self.requestMetrics = [[QNUploadRegionRequestMetrics alloc] initWithRegion:self.region];
|
||||
[self.requestMetrics start];
|
||||
[self performRequest:[self getNextServer:nil]
|
||||
action:action
|
||||
headers:headers
|
||||
method:@"PUT"
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
|
||||
- (void)performRequest:(id <QNUploadServer>)server
|
||||
action:(NSString *)action
|
||||
headers:(NSDictionary *)headers
|
||||
method:(NSString *)method
|
||||
body:(NSData *)body
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRegionRequestCompleteHandler)complete{
|
||||
|
||||
if (!server.host || server.host.length == 0) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoWithSDKInteriorError:@"server error"];
|
||||
[self complete:responseInfo response:nil complete:complete];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *serverHost = server.host;
|
||||
NSString *serverIP = server.ip;
|
||||
|
||||
if (self.config.converter) {
|
||||
serverHost = self.config.converter(serverHost);
|
||||
serverIP = nil;
|
||||
}
|
||||
|
||||
self.currentServer = server;
|
||||
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
NSString *urlString = [NSString stringWithFormat:@"%@%@", [QNUrlUtils setHostScheme:serverHost useHttps:self.config.useHttps], action ?: @""];
|
||||
request.URL = [NSURL URLWithString:urlString];
|
||||
request.HTTPMethod = method;
|
||||
[request setAllHTTPHeaderFields:headers];
|
||||
[request setTimeoutInterval:self.config.timeoutInterval];
|
||||
request.HTTPBody = body;
|
||||
|
||||
QNLogInfo(@"key:%@ url:%@", self.requestInfo.key, request.URL);
|
||||
QNLogInfo(@"key:%@ headers:%@", self.requestInfo.key, headers);
|
||||
|
||||
kQNWeakSelf;
|
||||
[self.singleRequest request:request
|
||||
server:server
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, NSArray<QNUploadSingleRequestMetrics *> * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
[self.requestMetrics addMetricsList:metrics];
|
||||
|
||||
BOOL hijacked = metrics.lastObject.isMaybeHijacked || metrics.lastObject.isForsureHijacked;
|
||||
BOOL isSafeDnsSource = kQNIsDnsSourceCustom(metrics.lastObject.syncDnsSource) || kQNIsDnsSourceDoh(metrics.lastObject.syncDnsSource) || kQNIsDnsSourceDnsPod(metrics.lastObject.syncDnsSource);
|
||||
BOOL hijackedAndNeedRetry = hijacked && isSafeDnsSource;
|
||||
if (hijackedAndNeedRetry) {
|
||||
[self.region updateIpListFormHost:server.host];
|
||||
}
|
||||
|
||||
if ((shouldRetry(responseInfo, response)
|
||||
&& self.config.allowBackupHost
|
||||
&& responseInfo.couldRegionRetry) || hijackedAndNeedRetry) {
|
||||
|
||||
id <QNUploadServer> newServer = [self getNextServer:responseInfo];
|
||||
if (newServer) {
|
||||
QNAsyncRunAfter(self.config.retryInterval, kQNBackgroundQueue, ^{
|
||||
[self performRequest:newServer
|
||||
action:action
|
||||
headers:headers
|
||||
method:method
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
});
|
||||
} else if (complete) {
|
||||
[self complete:responseInfo response:response complete:complete];
|
||||
}
|
||||
} else if (complete) {
|
||||
[self complete:responseInfo response:response complete:complete];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)complete:(QNResponseInfo *)responseInfo
|
||||
response:(NSDictionary *)response
|
||||
complete:(QNRegionRequestCompleteHandler)completionHandler {
|
||||
[self.requestMetrics end];
|
||||
|
||||
if (completionHandler) {
|
||||
completionHandler(responseInfo, self.requestMetrics, response);
|
||||
}
|
||||
self.singleRequest = nil;
|
||||
}
|
||||
|
||||
//MARK: --
|
||||
- (id <QNUploadServer>)getNextServer:(QNResponseInfo *)responseInfo{
|
||||
|
||||
if (responseInfo.isTlsError) {
|
||||
self.requestState.isUseOldServer = YES;
|
||||
}
|
||||
|
||||
return [self.region getNextServer:[self.requestState copy] responseInfo:responseInfo freezeServer:self.currentServer];
|
||||
}
|
||||
|
||||
@end
|
||||
42
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpSingleRequest.h
generated
Normal file
42
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpSingleRequest.h
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// QNHttpRequest+SingleRequestRetry.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNUploadRequestInfo.h"
|
||||
#import "QNIUploadServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNUploadRequestState, QNResponseInfo, QNConfiguration, QNUploadOption, QNUpToken, QNUploadSingleRequestMetrics;
|
||||
|
||||
typedef void(^QNSingleRequestCompleteHandler)(QNResponseInfo * _Nullable responseInfo, NSArray <QNUploadSingleRequestMetrics *> * _Nullable metrics, NSDictionary * _Nullable response);
|
||||
|
||||
@interface QNHttpSingleRequest : NSObject
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
token:(QNUpToken *)token
|
||||
requestInfo:(QNUploadRequestInfo *)requestInfo
|
||||
requestState:(QNUploadRequestState *)requestState;
|
||||
|
||||
|
||||
/// 网络请求
|
||||
/// @param request 请求内容
|
||||
/// @param server server信息,目前仅用于日志统计
|
||||
/// @param shouldRetry 判断是否需要重试的block
|
||||
/// @param progress 上传进度回调
|
||||
/// @param complete 上传完成回调
|
||||
- (void)request:(NSURLRequest *)request
|
||||
server:(id <QNUploadServer>)server
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNSingleRequestCompleteHandler)complete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
323
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpSingleRequest.m
generated
Normal file
323
Pods/Qiniu/QiniuSDK/Http/Request/QNHttpSingleRequest.m
generated
Normal file
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// QNHttpRequest+SingleRequestRetry.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNVersion.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNHttpSingleRequest.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNNetworkStatusManager.h"
|
||||
#import "QNRequestClient.h"
|
||||
#import "QNUploadRequestState.h"
|
||||
|
||||
#import "QNConnectChecker.h"
|
||||
#import "QNDnsPrefetch.h"
|
||||
|
||||
#import "QNReportItem.h"
|
||||
|
||||
#import "QNCFHttpClient.h"
|
||||
#import "QNUploadSystemClient.h"
|
||||
#import "NSURLRequest+QNRequest.h"
|
||||
|
||||
|
||||
|
||||
@interface QNHttpSingleRequest()
|
||||
|
||||
@property(nonatomic, assign)int currentRetryTime;
|
||||
@property(nonatomic, strong)QNConfiguration *config;
|
||||
@property(nonatomic, strong)QNUploadOption *uploadOption;
|
||||
@property(nonatomic, strong)QNUpToken *token;
|
||||
@property(nonatomic, strong)QNUploadRequestInfo *requestInfo;
|
||||
@property(nonatomic, strong)QNUploadRequestState *requestState;
|
||||
|
||||
@property(nonatomic, strong)NSMutableArray <QNUploadSingleRequestMetrics *> *requestMetricsList;
|
||||
|
||||
@property(nonatomic, strong)id <QNRequestClient> client;
|
||||
|
||||
@end
|
||||
@implementation QNHttpSingleRequest
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
token:(QNUpToken *)token
|
||||
requestInfo:(QNUploadRequestInfo *)requestInfo
|
||||
requestState:(QNUploadRequestState *)requestState{
|
||||
if (self = [super init]) {
|
||||
_config = config;
|
||||
_uploadOption = uploadOption;
|
||||
_token = token;
|
||||
_requestInfo = requestInfo;
|
||||
_requestState = requestState;
|
||||
_currentRetryTime = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)request:(NSURLRequest *)request
|
||||
server:(id <QNUploadServer>)server
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNSingleRequestCompleteHandler)complete{
|
||||
|
||||
_currentRetryTime = 0;
|
||||
_requestMetricsList = [NSMutableArray array];
|
||||
[self retryRequest:request server:server shouldRetry:shouldRetry progress:progress complete:complete];
|
||||
}
|
||||
|
||||
- (void)retryRequest:(NSURLRequest *)request
|
||||
server:(id <QNUploadServer>)server
|
||||
shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNSingleRequestCompleteHandler)complete{
|
||||
|
||||
if (kQNIsHttp3(server.httpVersion)) {
|
||||
self.client = [[QNUploadSystemClient alloc] init];
|
||||
} else {
|
||||
if ([self shouldUseCFClient:request server:server]) {
|
||||
self.client = [[QNCFHttpClient alloc] init];
|
||||
} else {
|
||||
self.client = [[QNUploadSystemClient alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
BOOL (^checkCancelHandler)(void) = ^{
|
||||
kQNStrongSelf;
|
||||
|
||||
BOOL isCancelled = self.requestState.isUserCancel;
|
||||
if (!isCancelled && self.uploadOption.cancellationSignal) {
|
||||
isCancelled = self.uploadOption.cancellationSignal();
|
||||
}
|
||||
return isCancelled;
|
||||
};
|
||||
|
||||
QNLogInfo(@"key:%@ retry:%d url:%@", self.requestInfo.key, self.currentRetryTime, request.URL);
|
||||
|
||||
[self.client request:request server:server connectionProxy:self.config.proxy progress:^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
|
||||
kQNStrongSelf;
|
||||
|
||||
if (progress) {
|
||||
progress(totalBytesWritten, totalBytesExpectedToWrite);
|
||||
}
|
||||
|
||||
if (checkCancelHandler()) {
|
||||
self.requestState.isUserCancel = YES;
|
||||
[self.client cancel];
|
||||
}
|
||||
} complete:^(NSURLResponse *response, QNUploadSingleRequestMetrics *metrics, NSData * responseData, NSError * error) {
|
||||
kQNStrongSelf;
|
||||
|
||||
if (metrics) {
|
||||
[self.requestMetricsList addObject:metrics];
|
||||
}
|
||||
|
||||
QNResponseInfo *responseInfo = nil;
|
||||
if (checkCancelHandler()) {
|
||||
responseInfo = [QNResponseInfo cancelResponse];
|
||||
[self reportRequest:responseInfo server:server requestMetrics:metrics];
|
||||
[self complete:responseInfo server:server response:nil requestMetrics:metrics complete:complete];
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *responseDic = nil;
|
||||
if (responseData) {
|
||||
responseDic = [NSJSONSerialization JSONObjectWithData:responseData
|
||||
options:NSJSONReadingMutableLeaves
|
||||
error:nil];
|
||||
}
|
||||
|
||||
responseInfo = [[QNResponseInfo alloc] initWithResponseInfoHost:request.qn_domain
|
||||
response:(NSHTTPURLResponse *)response
|
||||
body:responseData
|
||||
error:error];
|
||||
BOOL isSafeDnsSource = kQNIsDnsSourceCustom(server.source) || kQNIsDnsSourceDoh(server.source) || kQNIsDnsSourceDnsPod(server.source);
|
||||
BOOL hijacked = responseInfo.isNotQiniu && !isSafeDnsSource;
|
||||
if (hijacked) {
|
||||
metrics.hijacked = kQNMetricsRequestHijacked;
|
||||
NSError *err = nil;
|
||||
metrics.syncDnsSource = [kQNDnsPrefetch prefetchHostBySafeDns:server.host error:&err];
|
||||
metrics.syncDnsError = err;
|
||||
}
|
||||
|
||||
if (!hijacked && [self shouldCheckConnect:responseInfo]) {
|
||||
// 网络状态检测
|
||||
QNUploadSingleRequestMetrics *connectCheckMetrics = [QNConnectChecker check];
|
||||
metrics.connectCheckMetrics = connectCheckMetrics;
|
||||
if (![QNConnectChecker isConnected:connectCheckMetrics]) {
|
||||
NSString *message = [NSString stringWithFormat:@"check origin statusCode:%d error:%@", responseInfo.statusCode, responseInfo.error];
|
||||
responseInfo = [QNResponseInfo errorResponseInfo:NSURLErrorNotConnectedToInternet errorDesc:message];
|
||||
} else if (!isSafeDnsSource) {
|
||||
metrics.hijacked = kQNMetricsRequestMaybeHijacked;
|
||||
NSError *err = nil;
|
||||
[kQNDnsPrefetch prefetchHostBySafeDns:server.host error:&err];
|
||||
metrics.syncDnsError = err;
|
||||
}
|
||||
}
|
||||
|
||||
[self reportRequest:responseInfo server:server requestMetrics:metrics];
|
||||
|
||||
QNLogInfo(@"key:%@ response:%@", self.requestInfo.key, responseInfo);
|
||||
if (shouldRetry(responseInfo, responseDic)
|
||||
&& self.currentRetryTime < self.config.retryMax
|
||||
&& responseInfo.couldHostRetry) {
|
||||
self.currentRetryTime += 1;
|
||||
QNAsyncRunAfter(self.config.retryInterval, kQNBackgroundQueue, ^{
|
||||
[self retryRequest:request server:server shouldRetry:shouldRetry progress:progress complete:complete];
|
||||
});
|
||||
} else {
|
||||
[self complete:responseInfo server:server response:responseDic requestMetrics:metrics complete:complete];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)shouldCheckConnect:(QNResponseInfo *)responseInfo {
|
||||
if (!kQNGlobalConfiguration.connectCheckEnable || [kQNGlobalConfiguration.connectCheckURLStrings count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return responseInfo.statusCode == kQNNetworkError ||
|
||||
responseInfo.statusCode == kQNUnexpectedSysCallError || // CF 内部部分错误码 归结到了调用错误
|
||||
responseInfo.statusCode == NSURLErrorTimedOut /* NSURLErrorTimedOut */ ||
|
||||
responseInfo.statusCode == -1003 /* NSURLErrorCannotFindHost */ ||
|
||||
responseInfo.statusCode == -1004 /* NSURLErrorCannotConnectToHost */ ||
|
||||
responseInfo.statusCode == -1005 /* NSURLErrorNetworkConnectionLost */ ||
|
||||
responseInfo.statusCode == -1006 /* NSURLErrorDNSLookupFailed */ ||
|
||||
responseInfo.statusCode == -1009 /* NSURLErrorNotConnectedToInternet */ ||
|
||||
responseInfo.statusCode == -1200 /* NSURLErrorSecureConnectionFailed */ ||
|
||||
responseInfo.statusCode == -1204 /* NSURLErrorServerCertificateNotYetValid */ ||
|
||||
responseInfo.statusCode == -1205 /* NSURLErrorClientCertificateRejected */;
|
||||
}
|
||||
|
||||
- (void)complete:(QNResponseInfo *)responseInfo
|
||||
server:(id<QNUploadServer>)server
|
||||
response:(NSDictionary *)response
|
||||
requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics
|
||||
complete:(QNSingleRequestCompleteHandler)complete {
|
||||
[self updateHostNetworkStatus:responseInfo server:server requestMetrics:requestMetrics];
|
||||
if (complete) {
|
||||
complete(responseInfo, [self.requestMetricsList copy], response);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldUseCFClient:(NSURLRequest *)request server:(id <QNUploadServer>)server {
|
||||
if (request.qn_isHttps && server.host.length > 0 && server.ip.length > 0) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK:-- 统计网络状态
|
||||
- (void)updateHostNetworkStatus:(QNResponseInfo *)responseInfo
|
||||
server:(id <QNUploadServer>)server
|
||||
requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics{
|
||||
long long bytes = requestMetrics.bytesSend.longLongValue;
|
||||
if (requestMetrics.startDate && requestMetrics.endDate && bytes >= 1024 * 1024) {
|
||||
double duration = [requestMetrics.endDate timeIntervalSinceDate:requestMetrics.startDate] * 1000;
|
||||
NSNumber *speed = [QNUtils calculateSpeed:bytes totalTime:duration];
|
||||
if (speed) {
|
||||
NSString *type = [QNNetworkStatusManager getNetworkStatusType:server.host ip:server.ip];
|
||||
[kQNNetworkStatusManager updateNetworkStatus:type speed:(int)(speed.longValue / 1000)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK:-- 统计quality日志
|
||||
- (void)reportRequest:(QNResponseInfo *)info
|
||||
server:(id <QNUploadServer>)server
|
||||
requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics {
|
||||
|
||||
if (! [self.requestInfo shouldReportRequestLog]) {
|
||||
return;
|
||||
}
|
||||
|
||||
QNUploadSingleRequestMetrics *requestMetricsP = requestMetrics ?: [QNUploadSingleRequestMetrics emptyMetrics];
|
||||
|
||||
NSInteger currentTimestamp = [QNUtils currentTimestamp];
|
||||
QNReportItem *item = [QNReportItem item];
|
||||
[item setReportValue:QNReportLogTypeRequest forKey:QNReportRequestKeyLogType];
|
||||
[item setReportValue:@(currentTimestamp/1000) forKey:QNReportRequestKeyUpTime];
|
||||
[item setReportValue:info.requestReportStatusCode forKey:QNReportRequestKeyStatusCode];
|
||||
[item setReportValue:info.reqId forKey:QNReportRequestKeyRequestId];
|
||||
[item setReportValue:requestMetricsP.request.qn_domain forKey:QNReportRequestKeyHost];
|
||||
[item setReportValue:requestMetricsP.remoteAddress forKey:QNReportRequestKeyRemoteIp];
|
||||
[item setReportValue:requestMetricsP.remotePort forKey:QNReportRequestKeyPort];
|
||||
[item setReportValue:self.requestInfo.bucket forKey:QNReportRequestKeyTargetBucket];
|
||||
[item setReportValue:self.requestInfo.key forKey:QNReportRequestKeyTargetKey];
|
||||
[item setReportValue:requestMetricsP.totalElapsedTime forKey:QNReportRequestKeyTotalElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalDnsTime forKey:QNReportRequestKeyDnsElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalConnectTime forKey:QNReportRequestKeyConnectElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalSecureConnectTime forKey:QNReportRequestKeyTLSConnectElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalRequestTime forKey:QNReportRequestKeyRequestElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalWaitTime forKey:QNReportRequestKeyWaitElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalWaitTime forKey:QNReportRequestKeyResponseElapsedTime];
|
||||
[item setReportValue:requestMetricsP.totalResponseTime forKey:QNReportRequestKeyResponseElapsedTime];
|
||||
[item setReportValue:self.requestInfo.fileOffset forKey:QNReportRequestKeyFileOffset];
|
||||
[item setReportValue:requestMetricsP.bytesSend forKey:QNReportRequestKeyBytesSent];
|
||||
[item setReportValue:requestMetricsP.totalBytes forKey:QNReportRequestKeyBytesTotal];
|
||||
[item setReportValue:@([QNUtils getCurrentProcessID]) forKey:QNReportRequestKeyPid];
|
||||
[item setReportValue:@([QNUtils getCurrentThreadID]) forKey:QNReportRequestKeyTid];
|
||||
[item setReportValue:self.requestInfo.targetRegionId forKey:QNReportRequestKeyTargetRegionId];
|
||||
[item setReportValue:self.requestInfo.currentRegionId forKey:QNReportRequestKeyCurrentRegionId];
|
||||
[item setReportValue:info.requestReportErrorType forKey:QNReportRequestKeyErrorType];
|
||||
NSString *errorDesc = info.requestReportErrorType ? info.message : nil;
|
||||
[item setReportValue:errorDesc forKey:QNReportRequestKeyErrorDescription];
|
||||
[item setReportValue:self.requestInfo.requestType forKey:QNReportRequestKeyUpType];
|
||||
[item setReportValue:[QNUtils systemName] forKey:QNReportRequestKeyOsName];
|
||||
[item setReportValue:[QNUtils systemVersion] forKey:QNReportRequestKeyOsVersion];
|
||||
[item setReportValue:[QNUtils sdkLanguage] forKey:QNReportRequestKeySDKName];
|
||||
[item setReportValue:[QNUtils sdkVersion] forKey:QNReportRequestKeySDKVersion];
|
||||
[item setReportValue:@([QNUtils currentTimestamp]) forKey:QNReportRequestKeyClientTime];
|
||||
[item setReportValue:[QNUtils getCurrentNetworkType] forKey:QNReportRequestKeyNetworkType];
|
||||
[item setReportValue:[QNUtils getCurrentSignalStrength] forKey:QNReportRequestKeySignalStrength];
|
||||
|
||||
[item setReportValue:server.source forKey:QNReportRequestKeyPrefetchedDnsSource];
|
||||
if (server.ipPrefetchedTime) {
|
||||
NSInteger prefetchTime = currentTimestamp/1000 - [server.ipPrefetchedTime integerValue];
|
||||
[item setReportValue:@(prefetchTime) forKey:QNReportRequestKeyPrefetchedBefore];
|
||||
}
|
||||
[item setReportValue:kQNDnsPrefetch.lastPrefetchedErrorMessage forKey:QNReportRequestKeyPrefetchedErrorMessage];
|
||||
|
||||
[item setReportValue:requestMetricsP.httpVersion forKey:QNReportRequestKeyHttpVersion];
|
||||
|
||||
if (!kQNGlobalConfiguration.connectCheckEnable) {
|
||||
[item setReportValue:@"disable" forKey:QNReportRequestKeyNetworkMeasuring];
|
||||
} else if (requestMetricsP.connectCheckMetrics) {
|
||||
QNUploadSingleRequestMetrics *metrics = requestMetricsP.connectCheckMetrics;
|
||||
NSString *connectCheckDuration = [NSString stringWithFormat:@"%.2lf", [metrics.totalElapsedTime doubleValue]];
|
||||
NSString *connectCheckStatusCode = @"";
|
||||
if (metrics.response) {
|
||||
connectCheckStatusCode = [NSString stringWithFormat:@"%ld", (long)((NSHTTPURLResponse *)metrics.response).statusCode];
|
||||
} else if (metrics.error) {
|
||||
connectCheckStatusCode = [NSString stringWithFormat:@"%ld", (long)metrics.error.code];
|
||||
}
|
||||
NSString *networkMeasuring = [NSString stringWithFormat:@"duration:%@ status_code:%@",connectCheckDuration, connectCheckStatusCode];
|
||||
[item setReportValue:networkMeasuring forKey:QNReportRequestKeyNetworkMeasuring];
|
||||
}
|
||||
// 劫持标记
|
||||
[item setReportValue:requestMetricsP.hijacked forKey:QNReportRequestKeyHijacking];
|
||||
[item setReportValue:requestMetricsP.syncDnsSource forKey:QNReportRequestKeyDnsSource];
|
||||
[item setReportValue:[requestMetricsP.syncDnsError description] forKey:QNReportRequestKeyDnsErrorMessage];
|
||||
|
||||
// 成功统计速度
|
||||
if (info.isOK) {
|
||||
[item setReportValue:requestMetricsP.perceptiveSpeed forKey:QNReportRequestKeyPerceptiveSpeed];
|
||||
}
|
||||
|
||||
[item setReportValue:self.client.clientId forKey:QNReportRequestKeyHttpClient];
|
||||
|
||||
[kQNReporter reportItem:item token:self.token.token];
|
||||
}
|
||||
|
||||
@end
|
||||
28
Pods/Qiniu/QiniuSDK/Http/Request/QNIUploadServer.h
generated
Normal file
28
Pods/Qiniu/QiniuSDK/Http/Request/QNIUploadServer.h
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// QNIUploadServer.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/7/3.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import<Foundation/Foundation.h>
|
||||
|
||||
@protocol QNUploadServer <NSObject>
|
||||
|
||||
@property(nonatomic, copy, nullable, readonly)NSString *httpVersion;
|
||||
@property(nonatomic, copy, nullable, readonly)NSString *serverId;
|
||||
@property(nonatomic, copy, nullable, readonly)NSString *ip;
|
||||
@property(nonatomic, copy, nullable, readonly)NSString *host;
|
||||
@property(nonatomic, copy, nullable, readonly)NSString *source;
|
||||
@property(nonatomic,strong, nullable, readonly)NSNumber *ipPrefetchedTime;
|
||||
|
||||
@end
|
||||
|
||||
#define kQNHttpVersion1 @"http_version_1"
|
||||
#define kQNHttpVersion2 @"http_version_2"
|
||||
#define kQNHttpVersion3 @"http_version_3"
|
||||
|
||||
BOOL kQNIsHttp3(NSString * _Nullable httpVersion);
|
||||
BOOL kQNIsHttp2(NSString * _Nullable httpVersion);
|
||||
|
||||
17
Pods/Qiniu/QiniuSDK/Http/Request/QNIUploadServer.m
generated
Normal file
17
Pods/Qiniu/QiniuSDK/Http/Request/QNIUploadServer.m
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// QNIUploadServer.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/2/4.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNIUploadServer.h"
|
||||
|
||||
BOOL kQNIsHttp3(NSString * _Nullable httpVersion) {
|
||||
return [httpVersion isEqualToString:kQNHttpVersion3];
|
||||
}
|
||||
|
||||
BOOL kQNIsHttp2(NSString * _Nullable httpVersion) {
|
||||
return [httpVersion isEqualToString:kQNHttpVersion2];
|
||||
}
|
||||
30
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestClient.h
generated
Normal file
30
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestClient.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// QNRequestClient.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/29.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^QNRequestClientCompleteHandler)(NSURLResponse * _Nullable, QNUploadSingleRequestMetrics * _Nullable, NSData * _Nullable, NSError * _Nullable);
|
||||
|
||||
@protocol QNRequestClient <NSObject>
|
||||
|
||||
// client 标识
|
||||
@property(nonatomic, copy, readonly)NSString *clientId;
|
||||
|
||||
- (void)request:(NSURLRequest *)request
|
||||
server:(_Nullable id <QNUploadServer>)server
|
||||
connectionProxy:(NSDictionary * _Nullable)connectionProxy
|
||||
progress:(void(^ _Nullable)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(_Nullable QNRequestClientCompleteHandler)complete;
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
102
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestTransaction.h
generated
Normal file
102
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestTransaction.h
generated
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// QNRequestTransaction.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNUploadRegionInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNUpToken, QNConfiguration, QNUploadOption, QNResponseInfo, QNUploadRegionRequestMetrics;
|
||||
|
||||
typedef void(^QNRequestTransactionCompleteHandler)(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response);
|
||||
|
||||
// 单个对象只能执行一个事务,多个事务需要创建多个事务对象完成
|
||||
@interface QNRequestTransaction : NSObject
|
||||
|
||||
//MARK:-- 构造方法
|
||||
- (instancetype)initWithHosts:(NSArray <NSString *> *)hosts
|
||||
regionId:(NSString * _Nullable)regionId
|
||||
token:(QNUpToken *)token;
|
||||
|
||||
//MARK:-- upload事务构造方法 选择
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
targetRegion:(id <QNUploadRegion>)targetRegion
|
||||
currentRegion:(id <QNUploadRegion>)currentRegion
|
||||
key:(NSString * _Nullable)key
|
||||
token:(QNUpToken *)token;
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
hosts:(NSArray <NSString *> *)hosts
|
||||
regionId:(NSString * _Nullable)regionId
|
||||
key:(NSString * _Nullable)key
|
||||
token:(QNUpToken *)token;
|
||||
|
||||
- (void)queryUploadHosts:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
- (void)uploadFormData:(NSData *)data
|
||||
fileName:(NSString *)fileName
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
- (void)makeBlock:(long long)blockOffset
|
||||
blockSize:(long long)blockSize
|
||||
firstChunkData:(NSData *)firstChunkData
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
- (void)uploadChunk:(NSString *)blockContext
|
||||
blockOffset:(long long)blockOffset
|
||||
chunkData:(NSData *)chunkData
|
||||
chunkOffset:(long long)chunkOffset
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
- (void)makeFile:(long long)fileSize
|
||||
fileName:(NSString *)fileName
|
||||
blockContexts:(NSArray <NSString *> *)blockContexts
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
|
||||
- (void)initPart:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
- (void)uploadPart:(NSString *)uploadId
|
||||
partIndex:(NSInteger)partIndex
|
||||
partData:(NSData *)partData
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
/**
|
||||
* partInfoArray
|
||||
* |_ NSDictionary : { "etag": "<Etag>", "partNumber": <PartNumber> }
|
||||
*/
|
||||
- (void)completeParts:(NSString *)fileName
|
||||
uploadId:(NSString *)uploadId
|
||||
partInfoArray:(NSArray <NSDictionary *> *)partInfoArray
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
/**
|
||||
* 上传日志
|
||||
*/
|
||||
- (void)reportLog:(NSData *)logData
|
||||
logClientId:(NSString *)logClientId
|
||||
complete:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
/**
|
||||
* 获取服务端配置
|
||||
*/
|
||||
- (void)serverConfig:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
/**
|
||||
* 获取服务端针对某个用户的配置
|
||||
*/
|
||||
- (void)serverUserConfig:(QNRequestTransactionCompleteHandler)complete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
545
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestTransaction.m
generated
Normal file
545
Pods/Qiniu/QiniuSDK/Http/Request/QNRequestTransaction.m
generated
Normal file
@@ -0,0 +1,545 @@
|
||||
//
|
||||
// QNRequestTransaction.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/4/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNRequestTransaction.h"
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNCrc32.h"
|
||||
#import "NSData+QNMD5.h"
|
||||
#import "QNUrlSafeBase64.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUserAgent.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUploadRequestState.h"
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
|
||||
#import "QNUploadDomainRegion.h"
|
||||
#import "QNHttpRegionRequest.h"
|
||||
|
||||
@interface QNRequestTransaction()
|
||||
|
||||
@property(nonatomic, strong)QNConfiguration *config;
|
||||
@property(nonatomic, strong)QNUploadOption *uploadOption;
|
||||
@property(nonatomic, copy)NSString *key;
|
||||
@property(nonatomic, strong)QNUpToken *token;
|
||||
|
||||
@property(nonatomic, strong)QNUploadRequestInfo *requestInfo;
|
||||
@property(nonatomic, strong)QNUploadRequestState *requestState;
|
||||
@property(nonatomic, strong)QNHttpRegionRequest *regionRequest;
|
||||
|
||||
@end
|
||||
@implementation QNRequestTransaction
|
||||
|
||||
- (instancetype)initWithHosts:(NSArray <NSString *> *)hosts
|
||||
regionId:(NSString * _Nullable)regionId
|
||||
token:(QNUpToken *)token{
|
||||
return [self initWithConfig:[QNConfiguration defaultConfiguration]
|
||||
uploadOption:[QNUploadOption defaultOptions]
|
||||
hosts:hosts
|
||||
regionId:regionId
|
||||
key:nil
|
||||
token:token];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
hosts:(NSArray <NSString *> *)hosts
|
||||
regionId:(NSString * _Nullable)regionId
|
||||
key:(NSString * _Nullable)key
|
||||
token:(nonnull QNUpToken *)token{
|
||||
|
||||
QNUploadDomainRegion *region = [[QNUploadDomainRegion alloc] init];
|
||||
[region setupRegionData:[QNZoneInfo zoneInfoWithMainHosts:hosts regionId:regionId]];
|
||||
return [self initWithConfig:config
|
||||
uploadOption:uploadOption
|
||||
targetRegion:region
|
||||
currentRegion:region
|
||||
key:key
|
||||
token:token];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config
|
||||
uploadOption:(QNUploadOption *)uploadOption
|
||||
targetRegion:(id <QNUploadRegion>)targetRegion
|
||||
currentRegion:(id <QNUploadRegion>)currentRegion
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token{
|
||||
if (self = [super init]) {
|
||||
_config = config;
|
||||
_uploadOption = uploadOption;
|
||||
_requestState = [[QNUploadRequestState alloc] init];
|
||||
_key = key;
|
||||
_token = token;
|
||||
_requestInfo = [[QNUploadRequestInfo alloc] init];
|
||||
_requestInfo.targetRegionId = targetRegion.zoneInfo.regionId;
|
||||
_requestInfo.currentRegionId = currentRegion.zoneInfo.regionId;
|
||||
_requestInfo.bucket = token.bucket;
|
||||
_requestInfo.key = key;
|
||||
_regionRequest = [[QNHttpRegionRequest alloc] initWithConfig:config
|
||||
uploadOption:uploadOption
|
||||
token:token
|
||||
region:currentRegion
|
||||
requestInfo:_requestInfo
|
||||
requestState:_requestState];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//MARK: -- uc query
|
||||
- (void)queryUploadHosts:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeUCQuery;
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)!responseInfo.isOK;
|
||||
};
|
||||
|
||||
NSDictionary *header = @{@"User-Agent" : [kQNUserAgent getUserAgent:self.token.token]};
|
||||
NSString *action = [NSString stringWithFormat:@"/v4/query?ak=%@&bucket=%@&sdk_name=%@&sdk_version=%@", self.token.access, self.token.bucket, [QNUtils sdkLanguage], [QNUtils sdkVersion]];
|
||||
[self.regionRequest get:action
|
||||
headers:header
|
||||
shouldRetry:shouldRetry
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
//MARK: -- upload form
|
||||
- (void)uploadFormData:(NSData *)data
|
||||
fileName:(NSString *)fileName
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeForm;
|
||||
|
||||
NSMutableDictionary *param = [NSMutableDictionary dictionary];
|
||||
if (self.uploadOption.params) {
|
||||
[param addEntriesFromDictionary:self.uploadOption.params];
|
||||
}
|
||||
if (self.uploadOption.metaDataParam) {
|
||||
[param addEntriesFromDictionary:self.uploadOption.metaDataParam];
|
||||
}
|
||||
if (self.key && self.key.length > 0) {
|
||||
param[@"key"] = self.key;
|
||||
}
|
||||
param[@"token"] = self.token.token ?: @"";
|
||||
if (self.uploadOption.checkCrc) {
|
||||
param[@"crc32"] = [NSString stringWithFormat:@"%u", (unsigned int)[QNCrc32 data:data]];
|
||||
}
|
||||
|
||||
NSString *boundary = @"werghnvt54wef654rjuhgb56trtg34tweuyrgf";
|
||||
NSString *disposition = @"Content-Disposition: form-data";
|
||||
|
||||
NSMutableData *body = [NSMutableData data];
|
||||
@try {
|
||||
for (NSString *paramsKey in param) {
|
||||
NSString *pair = [NSString stringWithFormat:@"--%@\r\n%@; name=\"%@\"\r\n\r\n", boundary, disposition, paramsKey];
|
||||
[body appendData:[pair dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
id value = [param objectForKey:paramsKey];
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
[body appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
} else if ([value isKindOfClass:[NSData class]]) {
|
||||
[body appendData:value];
|
||||
}
|
||||
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
fileName = [QNUtils formEscape:fileName];
|
||||
|
||||
NSString *filePair = [NSString stringWithFormat:@"--%@\r\n%@; name=\"%@\"; filename=\"%@\"\nContent-Type:%@\r\n\r\n", boundary, disposition, @"file", fileName, self.uploadOption.mimeType];
|
||||
[body appendData:[filePair dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
[body appendData:data];
|
||||
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
} @catch (NSException *exception) {
|
||||
if (complete) {
|
||||
QNResponseInfo *info = [QNResponseInfo responseInfoWithLocalIOError:[NSString stringWithFormat:@"%@", exception]];
|
||||
QNUploadRegionRequestMetrics *metrics = [QNUploadRegionRequestMetrics emptyMetrics];
|
||||
complete(info, metrics, nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Content-Type"] = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
|
||||
header[@"Content-Length"] = [NSString stringWithFormat:@"%lu", (unsigned long)body.length];
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)!responseInfo.isOK;
|
||||
};
|
||||
|
||||
[self.regionRequest post:nil
|
||||
headers:header
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
//MARK: -- 分块上传
|
||||
- (void)makeBlock:(long long)blockOffset
|
||||
blockSize:(long long)blockSize
|
||||
firstChunkData:(NSData *)firstChunkData
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeMkblk;
|
||||
self.requestInfo.fileOffset = @(blockOffset);
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/octet-stream";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *action = [NSString stringWithFormat:@"/mkblk/%u", (unsigned int)blockSize];
|
||||
|
||||
NSString *chunkCrc = [NSString stringWithFormat:@"%u", (unsigned int)[QNCrc32 data:firstChunkData]];
|
||||
|
||||
kQNWeakSelf;
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
kQNStrongSelf;
|
||||
|
||||
NSString *ctx = response[@"ctx"];
|
||||
NSString *crcServer = [NSString stringWithFormat:@"%@", response[@"crc32"]];
|
||||
return (BOOL)(responseInfo.isOK == false || (responseInfo.isOK && (!ctx || (self.uploadOption.checkCrc && ![chunkCrc isEqualToString:crcServer]))));
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:firstChunkData
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
- (void)uploadChunk:(NSString *)blockContext
|
||||
blockOffset:(long long)blockOffset
|
||||
chunkData:(NSData *)chunkData
|
||||
chunkOffset:(long long)chunkOffset
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeBput;
|
||||
self.requestInfo.fileOffset = @(blockOffset + chunkOffset);
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/octet-stream";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *action = [NSString stringWithFormat:@"/bput/%@/%lld", blockContext, chunkOffset];
|
||||
|
||||
NSString *chunkCrc = [NSString stringWithFormat:@"%u", (unsigned int)[QNCrc32 data:chunkData]];
|
||||
|
||||
kQNWeakSelf;
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
kQNStrongSelf;
|
||||
|
||||
NSString *ctx = response[@"ctx"];
|
||||
NSString *crcServer = [NSString stringWithFormat:@"%@", response[@"crc32"]];
|
||||
return (BOOL)(responseInfo.isOK == false || (responseInfo.isOK && (!ctx || (self.uploadOption.checkCrc && ![chunkCrc isEqualToString:crcServer]))));
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:chunkData
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
- (void)makeFile:(long long)fileSize
|
||||
fileName:(NSString *)fileName
|
||||
blockContexts:(NSArray <NSString *> *)blockContexts
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeMkfile;
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/octet-stream";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *mimeType = [[NSString alloc] initWithFormat:@"/mimeType/%@", [QNUrlSafeBase64 encodeString:self.uploadOption.mimeType]];
|
||||
|
||||
__block NSString *action = [[NSString alloc] initWithFormat:@"/mkfile/%lld%@", fileSize, mimeType];
|
||||
|
||||
if (self.key != nil) {
|
||||
NSString *keyStr = [[NSString alloc] initWithFormat:@"/key/%@", [QNUrlSafeBase64 encodeString:self.key]];
|
||||
action = [NSString stringWithFormat:@"%@%@", action, keyStr];
|
||||
}
|
||||
|
||||
[self.uploadOption.params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
|
||||
action = [NSString stringWithFormat:@"%@/%@/%@", action, key, [QNUrlSafeBase64 encodeString:obj]];
|
||||
}];
|
||||
|
||||
[self.uploadOption.metaDataParam enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
|
||||
action = [NSString stringWithFormat:@"%@/%@/%@", action, key, [QNUrlSafeBase64 encodeString:obj]];
|
||||
}];
|
||||
|
||||
//添加路径
|
||||
NSString *fname = [[NSString alloc] initWithFormat:@"/fname/%@", [QNUrlSafeBase64 encodeString:fileName]];
|
||||
action = [NSString stringWithFormat:@"%@%@", action, fname];
|
||||
|
||||
NSMutableData *body = [NSMutableData data];
|
||||
NSString *bodyString = [blockContexts componentsJoinedByString:@","];
|
||||
[body appendData:[bodyString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:complete];
|
||||
}
|
||||
|
||||
|
||||
- (void)initPart:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeInitParts;
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/octet-stream";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *buckets = [[NSString alloc] initWithFormat:@"/buckets/%@", self.token.bucket];
|
||||
NSString *objects = [[NSString alloc] initWithFormat:@"/objects/%@", [self resumeV2EncodeKey:self.key]];;
|
||||
NSString *action = [[NSString alloc] initWithFormat:@"%@%@/uploads", buckets, objects];
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:nil
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)uploadPart:(NSString *)uploadId
|
||||
partIndex:(NSInteger)partIndex
|
||||
partData:(NSData *)partData
|
||||
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeUploadPart;
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/octet-stream";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
if (self.uploadOption.checkCrc) {
|
||||
NSString *md5 = [[partData qn_md5] lowercaseString];
|
||||
if (md5) {
|
||||
header[@"Content-MD5"] = md5;
|
||||
}
|
||||
}
|
||||
NSString *buckets = [[NSString alloc] initWithFormat:@"/buckets/%@", self.token.bucket];
|
||||
NSString *objects = [[NSString alloc] initWithFormat:@"/objects/%@", [self resumeV2EncodeKey:self.key]];;
|
||||
NSString *uploads = [[NSString alloc] initWithFormat:@"/uploads/%@", uploadId];
|
||||
NSString *partNumber = [[NSString alloc] initWithFormat:@"/%ld", (long)partIndex];
|
||||
NSString *action = [[NSString alloc] initWithFormat:@"%@%@%@%@", buckets, objects, uploads, partNumber];
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
NSString *etag = [NSString stringWithFormat:@"%@", response[@"etag"]];
|
||||
NSString *serverMD5 = [NSString stringWithFormat:@"%@", response[@"md5"]];
|
||||
return (BOOL)(!responseInfo.isOK || !etag || !serverMD5);
|
||||
};
|
||||
|
||||
[self.regionRequest put:action
|
||||
headers:header
|
||||
body:partData
|
||||
shouldRetry:shouldRetry
|
||||
progress:progress
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)completeParts:(NSString *)fileName
|
||||
uploadId:(NSString *)uploadId
|
||||
partInfoArray:(NSArray <NSDictionary *> *)partInfoArray
|
||||
complete:(QNRequestTransactionCompleteHandler)complete{
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeCompletePart;
|
||||
|
||||
if (!partInfoArray || partInfoArray.count == 0) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoWithInvalidArgument:@"partInfoArray"];
|
||||
if (complete) {
|
||||
complete(responseInfo, nil, responseInfo.responseDictionary);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/json";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *buckets = [[NSString alloc] initWithFormat:@"/buckets/%@", self.token.bucket];
|
||||
NSString *objects = [[NSString alloc] initWithFormat:@"/objects/%@", [self resumeV2EncodeKey:self.key]];
|
||||
NSString *uploads = [[NSString alloc] initWithFormat:@"/uploads/%@", uploadId];
|
||||
|
||||
NSString *action = [[NSString alloc] initWithFormat:@"%@%@%@", buckets, objects, uploads];
|
||||
|
||||
NSMutableDictionary *bodyDictionary = [NSMutableDictionary dictionary];
|
||||
if (partInfoArray) {
|
||||
bodyDictionary[@"parts"] = partInfoArray;
|
||||
}
|
||||
if (fileName) {
|
||||
bodyDictionary[@"fname"] = fileName;
|
||||
}
|
||||
if (self.uploadOption.mimeType) {
|
||||
bodyDictionary[@"mimeType"] = self.uploadOption.mimeType;
|
||||
}
|
||||
if (self.uploadOption.params) {
|
||||
bodyDictionary[@"customVars"] = self.uploadOption.params;
|
||||
}
|
||||
if (self.uploadOption.metaDataParam) {
|
||||
bodyDictionary[@"metaData"] = self.uploadOption.metaDataParam;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *body = [NSJSONSerialization dataWithJSONObject:bodyDictionary
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
if (error) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoWithLocalIOError:error.description];
|
||||
if (complete) {
|
||||
complete(responseInfo, nil, responseInfo.responseDictionary);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:body
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reportLog:(NSData *)logData
|
||||
logClientId:(NSString *)logClientId
|
||||
complete:(QNRequestTransactionCompleteHandler)complete {
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeUpLog;
|
||||
NSString *token = [NSString stringWithFormat:@"UpToken %@", self.token.token];
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"Authorization"] = token;
|
||||
header[@"Content-Type"] = @"application/json";
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *action = @"/log/4?compressed=gzip";
|
||||
|
||||
if (logClientId) {
|
||||
header[@"X-Log-Client-Id"] = logClientId;
|
||||
}
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:logData
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)serverConfig:(QNRequestTransactionCompleteHandler)complete {
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeServerConfig;
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *action = [NSString stringWithFormat:@"/v1/sdk/config?sdk_name=%@&sdk_version=%@", [QNUtils sdkLanguage], [QNUtils sdkVersion]];
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:nil
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)serverUserConfig:(QNRequestTransactionCompleteHandler)complete {
|
||||
|
||||
self.requestInfo.requestType = QNUploadRequestTypeServerUserConfig;
|
||||
NSMutableDictionary *header = [NSMutableDictionary dictionary];
|
||||
header[@"User-Agent"] = [kQNUserAgent getUserAgent:self.token.token];
|
||||
|
||||
NSString *action = [NSString stringWithFormat:@"/v1/sdk/config/user?ak=%@&sdk_name=%@&sdk_version=%@", self.token.access, [QNUtils sdkLanguage], [QNUtils sdkVersion]];
|
||||
|
||||
BOOL (^shouldRetry)(QNResponseInfo *, NSDictionary *) = ^(QNResponseInfo * responseInfo, NSDictionary * response){
|
||||
return (BOOL)(!responseInfo.isOK);
|
||||
};
|
||||
|
||||
[self.regionRequest post:action
|
||||
headers:header
|
||||
body:nil
|
||||
shouldRetry:shouldRetry
|
||||
progress:nil
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
|
||||
complete(responseInfo, metrics, response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)resumeV2EncodeKey:(NSString *)key{
|
||||
NSString *encodeKey = nil;
|
||||
if (!self.key) {
|
||||
encodeKey = @"~";
|
||||
} else if (self.key.length == 0) {
|
||||
encodeKey = @"";
|
||||
} else {
|
||||
encodeKey = [QNUrlSafeBase64 encodeString:self.key];
|
||||
}
|
||||
return encodeKey;
|
||||
}
|
||||
|
||||
@end
|
||||
30
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRegionInfo.h
generated
Normal file
30
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRegionInfo.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// QNUploadRegion.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/4/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNIUploadServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNZoneInfo, QNUploadRequestState, QNResponseInfo;
|
||||
|
||||
@protocol QNUploadRegion <NSObject>
|
||||
|
||||
@property(nonatomic, assign, readonly)BOOL isValid;
|
||||
@property(nonatomic, strong, nullable, readonly)QNZoneInfo *zoneInfo;
|
||||
|
||||
- (void)setupRegionData:(QNZoneInfo * _Nullable)zoneInfo;
|
||||
|
||||
- (id<QNUploadServer> _Nullable)getNextServer:(QNUploadRequestState *)requestState
|
||||
responseInfo:(QNResponseInfo *)responseInfo
|
||||
freezeServer:(id <QNUploadServer> _Nullable)freezeServer;
|
||||
|
||||
- (void)updateIpListFormHost:(NSString *)host;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
45
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRequestInfo.h
generated
Normal file
45
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRequestInfo.h
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// QNUploadRequestInfo.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/13.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadRequestInfo : NSObject
|
||||
|
||||
/// 当前请求的类型
|
||||
@property(nonatomic, copy, nullable)NSString *requestType;
|
||||
|
||||
/// 上传的bucket
|
||||
@property(nonatomic, copy, nullable)NSString *bucket;
|
||||
/// 上传的key
|
||||
@property(nonatomic, copy, nullable)NSString *key;
|
||||
/// 上传数据的偏移量
|
||||
@property(nonatomic, strong, nullable)NSNumber *fileOffset;
|
||||
/// 上传的目标region
|
||||
@property(nonatomic, copy, nullable)NSString *targetRegionId;
|
||||
/// 当前上传的region
|
||||
@property(nonatomic, copy, nullable)NSString *currentRegionId;
|
||||
|
||||
- (BOOL)shouldReportRequestLog;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const QNUploadRequestTypeUCQuery;
|
||||
extern NSString *const QNUploadRequestTypeForm;
|
||||
extern NSString *const QNUploadRequestTypeMkblk;
|
||||
extern NSString *const QNUploadRequestTypeBput;
|
||||
extern NSString *const QNUploadRequestTypeMkfile;
|
||||
extern NSString *const QNUploadRequestTypeInitParts;
|
||||
extern NSString *const QNUploadRequestTypeUploadPart;
|
||||
extern NSString *const QNUploadRequestTypeCompletePart;
|
||||
extern NSString *const QNUploadRequestTypeServerConfig;
|
||||
extern NSString *const QNUploadRequestTypeServerUserConfig;
|
||||
extern NSString *const QNUploadRequestTypeUpLog;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
29
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRequestInfo.m
generated
Normal file
29
Pods/Qiniu/QiniuSDK/Http/Request/QNUploadRequestInfo.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// QNUploadRequestInfo.m
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/13.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRequestInfo.h"
|
||||
|
||||
@implementation QNUploadRequestInfo
|
||||
|
||||
- (BOOL)shouldReportRequestLog{
|
||||
return ![self.requestType isEqualToString:QNUploadRequestTypeUpLog];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NSString * const QNUploadRequestTypeUCQuery = @"uc_query";
|
||||
NSString * const QNUploadRequestTypeForm = @"form";
|
||||
NSString * const QNUploadRequestTypeMkblk = @"mkblk";
|
||||
NSString * const QNUploadRequestTypeBput = @"bput";
|
||||
NSString * const QNUploadRequestTypeMkfile = @"mkfile";
|
||||
NSString * const QNUploadRequestTypeInitParts = @"init_parts";
|
||||
NSString * const QNUploadRequestTypeUploadPart = @"upload_part";
|
||||
NSString * const QNUploadRequestTypeCompletePart = @"complete_part";
|
||||
NSString * const QNUploadRequestTypeServerConfig = @"server_config";
|
||||
NSString * const QNUploadRequestTypeServerUserConfig = @"server_user_config";
|
||||
NSString * const QNUploadRequestTypeUpLog = @"uplog";
|
||||
21
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadDomainRegion.h
generated
Normal file
21
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadDomainRegion.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// QNUploadServerDomainResolver.h
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/23.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRegionInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNConfiguration;
|
||||
|
||||
@interface QNUploadDomainRegion : NSObject <QNUploadRegion>
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
444
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadDomainRegion.m
generated
Normal file
444
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadDomainRegion.m
generated
Normal file
@@ -0,0 +1,444 @@
|
||||
//
|
||||
// QNUploadServerDomainResolver.m
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/23.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRequestState.h"
|
||||
#import "QNUploadDomainRegion.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUploadServer.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUploadServerFreezeUtil.h"
|
||||
#import "QNUploadServerFreezeManager.h"
|
||||
#import "QNDnsPrefetch.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNDefine.h"
|
||||
#import "QNUploadServerNetworkStatus.h"
|
||||
|
||||
@interface QNUploadIpGroup : NSObject
|
||||
@property(nonatomic, assign)int ipIndex;
|
||||
@property(nonatomic, copy, readonly)NSString *groupType;
|
||||
@property(nonatomic, strong, readonly)NSArray <id <QNIDnsNetworkAddress> > *ipList;
|
||||
@end
|
||||
@implementation QNUploadIpGroup
|
||||
- (instancetype)initWithGroupType:(NSString *)groupType
|
||||
ipList:(NSArray <id <QNIDnsNetworkAddress> > *)ipList{
|
||||
if (self = [super init]) {
|
||||
_groupType = groupType;
|
||||
_ipList = ipList;
|
||||
_ipIndex = -1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (id <QNIDnsNetworkAddress>)getServerIP{
|
||||
if (!self.ipList || self.ipList.count == 0) {
|
||||
return nil;
|
||||
} else {
|
||||
if (_ipIndex < 0 || _ipIndex > (self.ipList.count - 1)) {
|
||||
_ipIndex = arc4random()%self.ipList.count;
|
||||
}
|
||||
return self.ipList[_ipIndex];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface QNUploadServerDomain: NSObject
|
||||
@property(nonatomic, copy)NSString *host;
|
||||
@property(nonatomic, strong)NSArray <QNUploadIpGroup *> *ipGroupList;
|
||||
@end
|
||||
@implementation QNUploadServerDomain
|
||||
|
||||
+ (QNUploadServerDomain *)domain:(NSString *)host{
|
||||
QNUploadServerDomain *domain = [[QNUploadServerDomain alloc] init];
|
||||
domain.host = host;
|
||||
return domain;
|
||||
}
|
||||
|
||||
- (QNUploadServer *)getServerWithCondition:(BOOL(^)(NSString *host, QNUploadServer *server, QNUploadServer *filterServer))condition {
|
||||
|
||||
@synchronized (self) {
|
||||
if (!self.ipGroupList || self.ipGroupList.count == 0) {
|
||||
[self createIpGroupList];
|
||||
}
|
||||
}
|
||||
|
||||
QNUploadServer *server = nil;
|
||||
|
||||
// Host解析出IP时:
|
||||
if (self.ipGroupList && self.ipGroupList.count > 0) {
|
||||
for (QNUploadIpGroup *ipGroup in self.ipGroupList) {
|
||||
|
||||
id <QNIDnsNetworkAddress> inetAddress = [ipGroup getServerIP];
|
||||
QNUploadServer *filterServer = [QNUploadServer server:self.host
|
||||
ip:inetAddress.ipValue
|
||||
source:inetAddress.sourceValue
|
||||
ipPrefetchedTime:inetAddress.timestampValue];
|
||||
if (condition == nil || condition(self.host, server, filterServer)) {
|
||||
server = filterServer;
|
||||
}
|
||||
|
||||
if (condition == nil) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
// Host未解析出IP时:
|
||||
QNUploadServer *hostServer = [QNUploadServer server:self.host
|
||||
ip:nil
|
||||
source:nil
|
||||
ipPrefetchedTime:nil];
|
||||
if (condition == nil || condition(self.host, nil, hostServer)) {
|
||||
// 未解析时,没有可比性,直接返回自身,自身即为最优
|
||||
server = hostServer;
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
- (QNUploadServer *)getOneServer{
|
||||
if (!self.host || self.host.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
if (self.ipGroupList && self.ipGroupList.count > 0) {
|
||||
NSInteger index = arc4random()%self.ipGroupList.count;
|
||||
QNUploadIpGroup *ipGroup = self.ipGroupList[index];
|
||||
id <QNIDnsNetworkAddress> inetAddress = [ipGroup getServerIP];
|
||||
QNUploadServer *server = [QNUploadServer server:self.host ip:inetAddress.ipValue source:inetAddress.sourceValue ipPrefetchedTime:inetAddress.timestampValue];;
|
||||
return server;
|
||||
} else {
|
||||
return [QNUploadServer server:self.host ip:nil source:nil ipPrefetchedTime:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearIpGroupList {
|
||||
@synchronized (self) {
|
||||
self.ipGroupList = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createIpGroupList {
|
||||
|
||||
@synchronized (self) {
|
||||
if (self.ipGroupList && self.ipGroupList.count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get address List of host
|
||||
NSArray *inetAddresses = [kQNDnsPrefetch getInetAddressByHost:self.host];
|
||||
if (!inetAddresses || inetAddresses.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// address List to ipList of group & check ip network
|
||||
NSMutableDictionary *ipGroupInfos = [NSMutableDictionary dictionary];
|
||||
for (id <QNIDnsNetworkAddress> inetAddress in inetAddresses) {
|
||||
NSString *ipValue = inetAddress.ipValue;
|
||||
NSString *groupType = [QNUtils getIpType:ipValue host:self.host];
|
||||
if (groupType) {
|
||||
NSMutableArray *ipList = ipGroupInfos[groupType] ?: [NSMutableArray array];
|
||||
[ipList addObject:inetAddress];
|
||||
ipGroupInfos[groupType] = ipList;
|
||||
}
|
||||
}
|
||||
|
||||
// ipList of group to ipGroup List
|
||||
NSMutableArray *ipGroupList = [NSMutableArray array];
|
||||
for (NSString *groupType in ipGroupInfos.allKeys) {
|
||||
NSArray *ipList = ipGroupInfos[groupType];
|
||||
QNUploadIpGroup *ipGroup = [[QNUploadIpGroup alloc] initWithGroupType:groupType ipList:ipList];
|
||||
[ipGroupList addObject:ipGroup];
|
||||
}
|
||||
|
||||
self.ipGroupList = ipGroupList;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNUploadDomainRegion()
|
||||
|
||||
// 是否支持http3
|
||||
@property(nonatomic, assign)BOOL http3Enabled;
|
||||
|
||||
// 是否冻结过Host,PS:如果没有冻结过 Host,则当前 Region 上传也就不会有错误信息,可能会返回-9,所以必须要再进行一次尝试
|
||||
@property(atomic , assign)BOOL hasFreezeHost;
|
||||
@property(atomic , assign)BOOL isAllFrozen;
|
||||
@property(atomic , assign)BOOL enableAccelerateUpload;
|
||||
// 局部http2冻结管理对象
|
||||
@property(nonatomic, strong)QNUploadServerFreezeManager *partialHttp2Freezer;
|
||||
@property(nonatomic, strong)QNUploadServerFreezeManager *partialHttp3Freezer;
|
||||
@property(nonatomic, strong)NSArray <NSString *> *accDomainHostList;
|
||||
@property(nonatomic, strong)NSDictionary <NSString *, QNUploadServerDomain *> *accDomainDictionary;
|
||||
@property(nonatomic, strong)NSArray <NSString *> *domainHostList;
|
||||
@property(nonatomic, strong)NSDictionary <NSString *, QNUploadServerDomain *> *domainDictionary;
|
||||
@property(nonatomic, strong)NSArray <NSString *> *oldDomainHostList;
|
||||
@property(nonatomic, strong)NSDictionary <NSString *, QNUploadServerDomain *> *oldDomainDictionary;
|
||||
|
||||
@property(nonatomic, strong, nullable)QNZoneInfo *zoneInfo;
|
||||
@end
|
||||
@implementation QNUploadDomainRegion
|
||||
|
||||
- (instancetype)initWithConfig:(QNConfiguration *)config {
|
||||
if (self = [super init]) {
|
||||
if (config) {
|
||||
self.enableAccelerateUpload = config.accelerateUploading;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isValid{
|
||||
return !self.isAllFrozen &&
|
||||
((self.enableAccelerateUpload && self.accDomainHostList > 0) ||
|
||||
self.domainHostList.count > 0 || self.oldDomainHostList.count > 0);
|
||||
}
|
||||
|
||||
- (void)setupRegionData:(QNZoneInfo *)zoneInfo{
|
||||
_zoneInfo = zoneInfo;
|
||||
|
||||
self.isAllFrozen = NO;
|
||||
self.hasFreezeHost = NO;
|
||||
self.http3Enabled = zoneInfo.http3Enabled;
|
||||
// 暂时屏蔽
|
||||
self.http3Enabled = false;
|
||||
|
||||
NSMutableArray *serverGroups = [NSMutableArray array];
|
||||
NSMutableArray *accDomainHostList = [NSMutableArray array];
|
||||
if (zoneInfo.acc_domains) {
|
||||
[serverGroups addObjectsFromArray:zoneInfo.acc_domains];
|
||||
[accDomainHostList addObjectsFromArray:zoneInfo.acc_domains];
|
||||
}
|
||||
self.accDomainHostList = accDomainHostList;
|
||||
self.accDomainDictionary = [self createDomainDictionary:serverGroups];
|
||||
|
||||
|
||||
[serverGroups removeAllObjects];
|
||||
NSMutableArray *domainHostList = [NSMutableArray array];
|
||||
if (zoneInfo.domains) {
|
||||
[serverGroups addObjectsFromArray:zoneInfo.domains];
|
||||
[domainHostList addObjectsFromArray:zoneInfo.domains];
|
||||
}
|
||||
self.domainHostList = domainHostList;
|
||||
self.domainDictionary = [self createDomainDictionary:serverGroups];
|
||||
|
||||
[serverGroups removeAllObjects];
|
||||
NSMutableArray *oldDomainHostList = [NSMutableArray array];
|
||||
if (zoneInfo.old_domains) {
|
||||
[serverGroups addObjectsFromArray:zoneInfo.old_domains];
|
||||
[oldDomainHostList addObjectsFromArray:zoneInfo.old_domains];
|
||||
}
|
||||
self.oldDomainHostList = oldDomainHostList;
|
||||
self.oldDomainDictionary = [self createDomainDictionary:serverGroups];
|
||||
|
||||
QNLogInfo(@"region :%@",domainHostList);
|
||||
QNLogInfo(@"region old:%@",oldDomainHostList);
|
||||
}
|
||||
|
||||
- (NSDictionary *)createDomainDictionary:(NSArray <NSString *> *)hosts{
|
||||
NSMutableDictionary *domainDictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
for (NSString *host in hosts) {
|
||||
QNUploadServerDomain *domain = [QNUploadServerDomain domain:host];
|
||||
[domainDictionary setObject:domain forKey:host];
|
||||
}
|
||||
return [domainDictionary copy];
|
||||
}
|
||||
|
||||
- (void)updateIpListFormHost:(NSString *)host {
|
||||
if (host == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.domainDictionary[host] clearIpGroupList];
|
||||
[self.oldDomainDictionary[host] clearIpGroupList];
|
||||
}
|
||||
|
||||
- (id<QNUploadServer> _Nullable)getNextServer:(QNUploadRequestState *)requestState
|
||||
responseInfo:(QNResponseInfo *)responseInfo
|
||||
freezeServer:(id <QNUploadServer> _Nullable)freezeServer{
|
||||
if (self.isAllFrozen) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self freezeServerIfNeed:responseInfo freezeServer:freezeServer];
|
||||
|
||||
BOOL accelerate = YES;
|
||||
@synchronized (self) {
|
||||
if (self.enableAccelerateUpload && responseInfo.isTransferAccelerationConfigureError) {
|
||||
self.enableAccelerateUpload = NO;
|
||||
}
|
||||
accelerate = self.enableAccelerateUpload;
|
||||
}
|
||||
|
||||
NSMutableArray *hostList = [NSMutableArray array];
|
||||
NSMutableDictionary *domainInfo = [NSMutableDictionary dictionary];
|
||||
if (requestState.isUseOldServer) {
|
||||
if (self.oldDomainHostList.count > 0 && self.oldDomainDictionary.count > 0) {
|
||||
[hostList addObjectsFromArray:self.oldDomainHostList];
|
||||
[domainInfo addEntriesFromDictionary:self.oldDomainDictionary];
|
||||
}
|
||||
} else {
|
||||
|
||||
// 优先使用 acc
|
||||
if (accelerate &&
|
||||
self.accDomainHostList.count > 0 &&
|
||||
self.accDomainDictionary.count > 0) {
|
||||
[hostList addObjectsFromArray:self.accDomainHostList];
|
||||
[domainInfo addEntriesFromDictionary:self.accDomainDictionary];
|
||||
}
|
||||
|
||||
if (self.domainHostList.count > 0 &&
|
||||
self.domainDictionary.count > 0){
|
||||
[hostList addObjectsFromArray:self.domainHostList];
|
||||
[domainInfo addEntriesFromDictionary:self.domainDictionary];
|
||||
}
|
||||
}
|
||||
|
||||
if (hostList.count == 0 || domainInfo.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
QNUploadServer *server = nil;
|
||||
// 1. 优先使用http3
|
||||
if (self.http3Enabled) {
|
||||
for (NSString *host in hostList) {
|
||||
QNUploadServer *domainServer = [domainInfo[host] getServerWithCondition:^BOOL(NSString *host, QNUploadServer *serverP, QNUploadServer *filterServer) {
|
||||
|
||||
// 1.1 剔除冻结对象
|
||||
NSString *frozenType = QNUploadFrozenType(host, filterServer.ip);
|
||||
BOOL isFrozen = [QNUploadServerFreezeUtil isType:frozenType
|
||||
frozenByFreezeManagers:@[self.partialHttp3Freezer, kQNUploadGlobalHttp3Freezer]];
|
||||
if (isFrozen) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 1.2 挑选网络状态最优
|
||||
return [QNUploadServerNetworkStatus isServerNetworkBetter:filterServer thanServerB:serverP];
|
||||
}];
|
||||
server = [QNUploadServerNetworkStatus getBetterNetworkServer:server serverB:domainServer];
|
||||
|
||||
if (server) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (server) {
|
||||
server.httpVersion = kQNHttpVersion3;
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2. 挑选http2
|
||||
for (NSString *host in hostList) {
|
||||
kQNWeakSelf;
|
||||
QNUploadServer *domainServer = [domainInfo[host] getServerWithCondition:^BOOL(NSString *host, QNUploadServer *serverP, QNUploadServer *filterServer) {
|
||||
kQNStrongSelf;
|
||||
|
||||
// 2.1 剔除冻结对象
|
||||
NSString *frozenType = QNUploadFrozenType(host, filterServer.ip);
|
||||
BOOL isFrozen = [QNUploadServerFreezeUtil isType:frozenType
|
||||
frozenByFreezeManagers:@[self.partialHttp2Freezer, kQNUploadGlobalHttp2Freezer]];
|
||||
if (isFrozen) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 2.2 挑选网络状态最优
|
||||
return [QNUploadServerNetworkStatus isServerNetworkBetter:filterServer thanServerB:serverP];
|
||||
}];
|
||||
server = [QNUploadServerNetworkStatus getBetterNetworkServer:server serverB:domainServer];
|
||||
|
||||
if (server) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 无可用 server 且未冻结过 Host 则随机获取一个
|
||||
if (server == nil && !self.hasFreezeHost && hostList.count > 0) {
|
||||
NSInteger index = arc4random()%hostList.count;
|
||||
NSString *host = hostList[index];
|
||||
server = [domainInfo[host] getOneServer];
|
||||
[self unfreezeServer:server];
|
||||
}
|
||||
|
||||
if (server == nil) {
|
||||
self.isAllFrozen = YES;
|
||||
}
|
||||
|
||||
server.httpVersion = kQNHttpVersion2;
|
||||
|
||||
QNLogInfo(@"get server host:%@ ip:%@", server.host, server.ip);
|
||||
return server;
|
||||
}
|
||||
|
||||
- (void)freezeServerIfNeed:(QNResponseInfo *)responseInfo
|
||||
freezeServer:(QNUploadServer *)freezeServer {
|
||||
|
||||
if (freezeServer == nil || freezeServer.serverId == nil || responseInfo == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *frozenType = QNUploadFrozenType(freezeServer.host, freezeServer.ip);
|
||||
// 1. http3 冻结
|
||||
if (kQNIsHttp3(freezeServer.httpVersion)) {
|
||||
if (responseInfo.isNotQiniu) {
|
||||
self.hasFreezeHost = YES;
|
||||
[self.partialHttp3Freezer freezeType:frozenType frozenTime:kQNGlobalConfiguration.partialHostFrozenTime];
|
||||
}
|
||||
|
||||
if (!responseInfo.canConnectToHost || responseInfo.isHostUnavailable) {
|
||||
self.hasFreezeHost = YES;
|
||||
[kQNUploadGlobalHttp3Freezer freezeType:frozenType frozenTime:kQNUploadHttp3FrozenTime];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. http2 冻结
|
||||
// 2.1 无法连接到Host || Host不可用, 局部冻结
|
||||
if (responseInfo.isNotQiniu || !responseInfo.canConnectToHost || responseInfo.isHostUnavailable) {
|
||||
QNLogInfo(@"partial freeze server host:%@ ip:%@", freezeServer.host, freezeServer.ip);
|
||||
self.hasFreezeHost = YES;
|
||||
[self.partialHttp2Freezer freezeType:frozenType frozenTime:kQNGlobalConfiguration.partialHostFrozenTime];
|
||||
}
|
||||
|
||||
// 2.2 Host不可用,全局冻结
|
||||
if (responseInfo.isHostUnavailable) {
|
||||
QNLogInfo(@"global freeze server host:%@ ip:%@", freezeServer.host, freezeServer.ip);
|
||||
self.hasFreezeHost = YES;
|
||||
[kQNUploadGlobalHttp2Freezer freezeType:frozenType frozenTime:kQNGlobalConfiguration.globalHostFrozenTime];
|
||||
}
|
||||
}
|
||||
|
||||
/// 仅仅解封局部的
|
||||
- (void)unfreezeServer:(QNUploadServer *)freezeServer {
|
||||
if (freezeServer == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *frozenType = QNUploadFrozenType(freezeServer.host, freezeServer.ip);
|
||||
[self.partialHttp2Freezer unfreezeType:frozenType];
|
||||
}
|
||||
|
||||
- (QNUploadServerFreezeManager *)partialHttp2Freezer{
|
||||
if (!_partialHttp2Freezer) {
|
||||
_partialHttp2Freezer = [[QNUploadServerFreezeManager alloc] init];
|
||||
}
|
||||
return _partialHttp2Freezer;
|
||||
}
|
||||
|
||||
- (QNUploadServerFreezeManager *)partialHttp3Freezer{
|
||||
if (!_partialHttp3Freezer) {
|
||||
_partialHttp3Freezer = [[QNUploadServerFreezeManager alloc] init];
|
||||
}
|
||||
return _partialHttp3Freezer;
|
||||
}
|
||||
|
||||
@end
|
||||
29
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServer.h
generated
Normal file
29
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServer.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// QNUploadServer.h
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/23.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadRegionInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadServer : NSObject <QNUploadServer>
|
||||
|
||||
@property(nonatomic, copy)NSString *httpVersion;
|
||||
|
||||
/// 上传server构造方法
|
||||
/// @param host host
|
||||
/// @param ip host对应的IP
|
||||
/// @param source ip查询来源,@"system",@"httpdns", @"none", @"customized" 自定义请使用@"customized"
|
||||
/// @param ipPrefetchedTime 根据host获取IP的时间戳
|
||||
+ (instancetype)server:(NSString * _Nullable)host
|
||||
ip:(NSString * _Nullable)ip
|
||||
source:(NSString * _Nullable)source
|
||||
ipPrefetchedTime:(NSNumber * _Nullable)ipPrefetchedTime;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
39
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServer.m
generated
Normal file
39
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServer.m
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// QNUploadServer.m
|
||||
// AppTest
|
||||
//
|
||||
// Created by yangsen on 2020/4/23.
|
||||
// Copyright © 2020 com.qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadServer.h"
|
||||
|
||||
@interface QNUploadServer()
|
||||
|
||||
@property(nonatomic, copy)NSString *ip;
|
||||
@property(nonatomic, copy)NSString *host;
|
||||
@property(nonatomic, copy)NSString *source;
|
||||
@property(nonatomic,strong)NSNumber *ipPrefetchedTime;
|
||||
|
||||
@end
|
||||
@implementation QNUploadServer
|
||||
@synthesize httpVersion;
|
||||
|
||||
+ (instancetype)server:(NSString *)host
|
||||
ip:(NSString *)ip
|
||||
source:(NSString *)source
|
||||
ipPrefetchedTime:(NSNumber *)ipPrefetchedTime{
|
||||
QNUploadServer *server = [[QNUploadServer alloc] init];
|
||||
server.ip = ip;
|
||||
server.host = host;
|
||||
server.source = source ?: @"none";
|
||||
server.httpVersion = kQNHttpVersion2;
|
||||
server.ipPrefetchedTime = ipPrefetchedTime;
|
||||
return server;
|
||||
}
|
||||
|
||||
- (NSString *)serverId {
|
||||
return [self.host copy];
|
||||
}
|
||||
|
||||
@end
|
||||
30
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeManager.h
generated
Normal file
30
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeManager.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// QNUploadServerFreezeManager.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/6/2.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUploadServerFreezeManager : NSObject
|
||||
|
||||
/// 查询host是否被冻结
|
||||
/// @param type 冻结Key
|
||||
- (BOOL)isTypeFrozen:(NSString * _Nullable)type;
|
||||
|
||||
/// 冻结host
|
||||
/// @param type 冻结Key
|
||||
/// @param frozenTime 冻结时间
|
||||
- (void)freezeType:(NSString * _Nullable)type frozenTime:(NSInteger)frozenTime;
|
||||
|
||||
/// 解冻host
|
||||
/// @param type 冻结Key
|
||||
- (void)unfreezeType:(NSString * _Nullable)type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
97
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeManager.m
generated
Normal file
97
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeManager.m
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// QNUploadServerFreezeManager.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/6/2.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUploadServerFreezeManager.h"
|
||||
|
||||
@interface QNUploadServerFreezeItem : NSObject
|
||||
@property(nonatomic, copy)NSString *type;
|
||||
@property(nonatomic, strong)NSDate *freezeDate;
|
||||
@end
|
||||
@implementation QNUploadServerFreezeItem
|
||||
+ (instancetype)item:(NSString *)type{
|
||||
QNUploadServerFreezeItem *item = [[QNUploadServerFreezeItem alloc] init];
|
||||
item.type = type;
|
||||
return item;
|
||||
}
|
||||
- (BOOL)isFrozenByDate:(NSDate *)date{
|
||||
BOOL isFrozen = YES;
|
||||
@synchronized (self) {
|
||||
if (!self.freezeDate || [self.freezeDate timeIntervalSinceDate:date] < 0){
|
||||
isFrozen = NO;
|
||||
}
|
||||
}
|
||||
return isFrozen;
|
||||
}
|
||||
- (void)freeze:(NSInteger)frozenTime{
|
||||
@synchronized (self) {
|
||||
self.freezeDate = [NSDate dateWithTimeIntervalSinceNow:frozenTime];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface QNUploadServerFreezeManager()
|
||||
|
||||
@property(nonatomic, strong)NSMutableDictionary *freezeInfo;
|
||||
|
||||
@end
|
||||
@implementation QNUploadServerFreezeManager
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_freezeInfo = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isTypeFrozen:(NSString * _Nullable)type {
|
||||
if (!type || type.length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL isFrozen = true;
|
||||
QNUploadServerFreezeItem *item = nil;
|
||||
@synchronized (self) {
|
||||
item = self.freezeInfo[type];
|
||||
}
|
||||
|
||||
if (!item || ![item isFrozenByDate:[NSDate date]]) {
|
||||
isFrozen = false;
|
||||
}
|
||||
|
||||
return isFrozen;
|
||||
}
|
||||
|
||||
- (void)freezeType:(NSString * _Nullable)type frozenTime:(NSInteger)frozenTime {
|
||||
if (!type || type.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QNUploadServerFreezeItem *item = nil;
|
||||
@synchronized (self) {
|
||||
item = self.freezeInfo[type];
|
||||
if (!item) {
|
||||
item = [QNUploadServerFreezeItem item:type];
|
||||
self.freezeInfo[type] = item;
|
||||
}
|
||||
}
|
||||
|
||||
[item freeze:frozenTime];
|
||||
}
|
||||
|
||||
- (void)unfreezeType:(NSString * _Nullable)type {
|
||||
if (!type || type.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
[self.freezeInfo removeObjectForKey:type];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
31
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeUtil.h
generated
Normal file
31
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeUtil.h
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// QNUploadServerFreezeUtil.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/2/4.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadServerFreezeManager.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define kQNUploadHttp3FrozenTime (3600 * 24)
|
||||
#define QNUploadFrozenType(HOST, IP) ([QNUploadServerFreezeUtil getFrozenType:HOST ip:IP])
|
||||
|
||||
#define kQNUploadGlobalHttp3Freezer [QNUploadServerFreezeUtil sharedHttp3Freezer]
|
||||
#define kQNUploadGlobalHttp2Freezer [QNUploadServerFreezeUtil sharedHttp2Freezer]
|
||||
|
||||
@interface QNUploadServerFreezeUtil : NSObject
|
||||
|
||||
+ (QNUploadServerFreezeManager *)sharedHttp2Freezer;
|
||||
+ (QNUploadServerFreezeManager *)sharedHttp3Freezer;
|
||||
|
||||
+ (BOOL)isType:(NSString *)type frozenByFreezeManagers:(NSArray <QNUploadServerFreezeManager *> *)freezeManagerList;
|
||||
|
||||
+ (NSString *)getFrozenType:(NSString *)host ip:(NSString *)ip;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
55
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeUtil.m
generated
Normal file
55
Pods/Qiniu/QiniuSDK/Http/ServerRegion/QNUploadServerFreezeUtil.m
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// QNUploadServerFreezeUtil.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/2/4.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUtils.h"
|
||||
#import "QNUploadServerFreezeUtil.h"
|
||||
|
||||
@implementation QNUploadServerFreezeUtil
|
||||
|
||||
+ (QNUploadServerFreezeManager *)sharedHttp2Freezer {
|
||||
static QNUploadServerFreezeManager *manager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[QNUploadServerFreezeManager alloc] init];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
+ (QNUploadServerFreezeManager *)sharedHttp3Freezer {
|
||||
static QNUploadServerFreezeManager *manager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[QNUploadServerFreezeManager alloc] init];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
+ (BOOL)isType:(NSString *)type frozenByFreezeManagers:(NSArray <QNUploadServerFreezeManager *> *)freezeManagerList{
|
||||
if (!type || type.length == 0) {
|
||||
return YES;
|
||||
}
|
||||
if (!freezeManagerList || freezeManagerList.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL isFrozen = NO;
|
||||
for (QNUploadServerFreezeManager *freezeManager in freezeManagerList) {
|
||||
isFrozen = [freezeManager isTypeFrozen:type];
|
||||
if (isFrozen) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isFrozen;
|
||||
}
|
||||
|
||||
+ (NSString *)getFrozenType:(NSString *)host ip:(NSString *)ip {
|
||||
NSString *ipType = [QNUtils getIpType:ip host:host];
|
||||
return [NSString stringWithFormat:@"%@-%@", host, ipType];
|
||||
}
|
||||
|
||||
@end
|
||||
36
Pods/Qiniu/QiniuSDK/PrivacyInfo.xcprivacy
generated
Normal file
36
Pods/Qiniu/QiniuSDK/PrivacyInfo.xcprivacy
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
26
Pods/Qiniu/QiniuSDK/QiniuSDK.h
generated
Normal file
26
Pods/Qiniu/QiniuSDK/QiniuSDK.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// QiniuSDK.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14-9-28.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "QNErrorCode.h"
|
||||
#import "QNDns.h"
|
||||
#import "QNZone.h"
|
||||
#import "QNFixedZone.h"
|
||||
#import "QNAutoZone.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNServerConfigMonitor.h"
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import "QNFileRecorder.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUploadManager.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNUrlSafeBase64.h"
|
||||
#import "QNReportConfig.h"
|
||||
#import "QNPipeline.h"
|
||||
55
Pods/Qiniu/QiniuSDK/Recorder/QNFileRecorder.h
generated
Executable file
55
Pods/Qiniu/QiniuSDK/Recorder/QNFileRecorder.h
generated
Executable file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// QNFileRecorder.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/5.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* 将上传记录保存到文件系统中
|
||||
*/
|
||||
@interface QNFileRecorder : NSObject <QNRecorderDelegate>
|
||||
|
||||
/**
|
||||
* 用指定保存的目录进行初始化
|
||||
*
|
||||
* @param directory 目录
|
||||
* @param error 输出的错误信息
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
/**
|
||||
* 用指定保存的目录,以及是否进行base64编码进行初始化,
|
||||
*
|
||||
* @param directory 目录
|
||||
* @param encode 为避免因为特殊字符或含有/,无法保存持久化记录,故用此参数指定是否要base64编码
|
||||
* @param error 输出错误信息
|
||||
*
|
||||
* @return 实例
|
||||
*/
|
||||
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
|
||||
encodeKey:(BOOL)encode
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
/**
|
||||
* 从外部手动删除记录,如无特殊需求,不建议使用
|
||||
*
|
||||
* @param key 持久化记录key
|
||||
* @param dir 目录
|
||||
* @param encode 是否encode
|
||||
*/
|
||||
+ (void)removeKey:(NSString *)key
|
||||
directory:(NSString *)dir
|
||||
encodeKey:(BOOL)encode;
|
||||
|
||||
// 删除所有修改时间在 date 之前的缓存
|
||||
- (NSError *)deleteAll;
|
||||
|
||||
@end
|
||||
127
Pods/Qiniu/QiniuSDK/Recorder/QNFileRecorder.m
generated
Normal file
127
Pods/Qiniu/QiniuSDK/Recorder/QNFileRecorder.m
generated
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// QNFileRecorder.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/5.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNFileRecorder.h"
|
||||
#import "QNUrlSafeBase64.h"
|
||||
|
||||
@interface QNFileRecorder ()
|
||||
|
||||
@property (copy, readonly) NSString *directory;
|
||||
@property BOOL encode;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNFileRecorder
|
||||
|
||||
- (NSString *)pathOfKey:(NSString *)key {
|
||||
return [QNFileRecorder pathJoin:key path:_directory];
|
||||
}
|
||||
|
||||
+ (NSString *)pathJoin:(NSString *)key
|
||||
path:(NSString *)path {
|
||||
return [[NSString alloc] initWithFormat:@"%@/%@", path, key];
|
||||
}
|
||||
|
||||
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
|
||||
error:(NSError *__autoreleasing *)perror {
|
||||
return [QNFileRecorder fileRecorderWithFolder:directory encodeKey:false error:perror];
|
||||
}
|
||||
|
||||
+ (instancetype)fileRecorderWithFolder:(NSString *)directory
|
||||
encodeKey:(BOOL)encode
|
||||
error:(NSError *__autoreleasing *)perror {
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
if (error != nil) {
|
||||
if (perror) {
|
||||
*perror = error;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [[QNFileRecorder alloc] initWithFolder:directory encodeKey:encode];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFolder:(NSString *)directory encodeKey:(BOOL)encode {
|
||||
if (self = [super init]) {
|
||||
_directory = directory;
|
||||
_encode = encode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSError *)set:(NSString *)key
|
||||
data:(NSData *)value {
|
||||
NSError *error;
|
||||
if (_encode) {
|
||||
key = [QNUrlSafeBase64 encodeString:key];
|
||||
}
|
||||
[value writeToFile:[self pathOfKey:key] options:NSDataWritingAtomic error:&error];
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSData *)get:(NSString *)key {
|
||||
if (_encode) {
|
||||
key = [QNUrlSafeBase64 encodeString:key];
|
||||
}
|
||||
return [NSData dataWithContentsOfFile:[self pathOfKey:key]];
|
||||
}
|
||||
|
||||
- (NSError *)del:(NSString *)key {
|
||||
NSError *error;
|
||||
if (_encode) {
|
||||
key = [QNUrlSafeBase64 encodeString:key];
|
||||
}
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self pathOfKey:key] error:&error];
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSError *)deleteAll {
|
||||
NSString *filePath = self.directory;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
BOOL isDir = NO;
|
||||
BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir];
|
||||
if ( !(isDir == YES && existed == YES) ) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
//文件夹
|
||||
NSError *error;
|
||||
[fileManager removeItemAtPath:filePath error:&error];
|
||||
if (error != nil) {
|
||||
return error;
|
||||
}
|
||||
|
||||
[fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSString *)getFileName{
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)removeKey:(NSString *)key
|
||||
directory:(NSString *)dir
|
||||
encodeKey:(BOOL)encode {
|
||||
if (encode) {
|
||||
key = [QNUrlSafeBase64 encodeString:key];
|
||||
}
|
||||
NSError *error;
|
||||
NSString *path = [QNFileRecorder pathJoin:key path:dir];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
|
||||
if (error) {
|
||||
NSLog(@"%s,%@", __func__, error);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, dir: %@>", NSStringFromClass([self class]), self, _directory];
|
||||
}
|
||||
|
||||
@end
|
||||
60
Pods/Qiniu/QiniuSDK/Recorder/QNRecorderDelegate.h
generated
Executable file
60
Pods/Qiniu/QiniuSDK/Recorder/QNRecorderDelegate.h
generated
Executable file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// QNRecorderDelegate.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 14/10/5.
|
||||
// Copyright (c) 2014年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* 为持久化上传记录,根据上传的key以及文件名 生成持久化的记录key
|
||||
*
|
||||
* @param uploadKey 上传的key
|
||||
* @param filePath 文件名
|
||||
*
|
||||
* @return 根据uploadKey, filepath 算出的记录key
|
||||
*/
|
||||
typedef NSString * (^QNRecorderKeyGenerator)(NSString *uploadKey, NSString *filePath);
|
||||
|
||||
/**
|
||||
* 持久化记录接口,可以实现将记录持久化到文件,数据库等
|
||||
*/
|
||||
@protocol QNRecorderDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* 保存记录
|
||||
*
|
||||
* @param key 持久化记录的key
|
||||
* @param value 持久化记录上传状态信息
|
||||
*
|
||||
* @return 错误信息,成功为nil
|
||||
*/
|
||||
- (NSError *)set:(NSString *)key
|
||||
data:(NSData *)value;
|
||||
|
||||
/**
|
||||
* 取出保存的持久化上传状态信息
|
||||
*
|
||||
* @param key 持久化记录key
|
||||
*
|
||||
* @return 上传中间状态信息
|
||||
*/
|
||||
- (NSData *)get:(NSString *)key;
|
||||
|
||||
/**
|
||||
* 删除持久化记录,一般在上传成功后自动调用
|
||||
*
|
||||
* @param key 持久化记录key
|
||||
*
|
||||
* @return 错误信息,成功为nil
|
||||
*/
|
||||
- (NSError *)del:(NSString *)key;
|
||||
|
||||
/**
|
||||
* 缓存文件名称
|
||||
*/
|
||||
- (NSString *)getFileName;
|
||||
|
||||
@end
|
||||
126
Pods/Qiniu/QiniuSDK/Storage/QNBaseUpload.h
generated
Normal file
126
Pods/Qiniu/QiniuSDK/Storage/QNBaseUpload.h
generated
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// QNBaseUpload.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2020/4/19.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNCrc32.h"
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNUrlSafeBase64.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNUploadManager.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNZone.h"
|
||||
#import "QNUploadSource.h"
|
||||
#import "QNUploadRequestMetrics.h"
|
||||
|
||||
extern NSString *const QNUploadUpTypeForm;
|
||||
extern NSString *const QNUploadUpTypeResumableV1;
|
||||
extern NSString *const QNUploadUpTypeResumableV2;
|
||||
|
||||
typedef void (^QNUpTaskCompletionHandler)(QNResponseInfo *info, NSString *key, QNUploadTaskMetrics *metrics, NSDictionary *resp);
|
||||
|
||||
@interface QNBaseUpload : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *upType;
|
||||
@property (nonatomic, copy, readonly) NSString *key;
|
||||
@property (nonatomic, copy, readonly) NSString *fileName;
|
||||
@property (nonatomic, strong, readonly) NSData *data;
|
||||
@property (nonatomic, strong, readonly) id <QNUploadSource> uploadSource;
|
||||
@property (nonatomic, strong, readonly) QNUpToken *token;
|
||||
@property (nonatomic, strong, readonly) QNUploadOption *option;
|
||||
@property (nonatomic, strong, readonly) QNConfiguration *config;
|
||||
@property (nonatomic, strong, readonly) id <QNRecorderDelegate> recorder;
|
||||
@property (nonatomic, copy, readonly) NSString *recorderKey;
|
||||
@property (nonatomic, strong, readonly) QNUpTaskCompletionHandler completionHandler;
|
||||
|
||||
@property (nonatomic, strong, readonly) QNUploadRegionRequestMetrics *currentRegionRequestMetrics;
|
||||
@property (nonatomic, strong, readonly) QNUploadTaskMetrics *metrics;
|
||||
|
||||
|
||||
//MARK:-- 构造函数
|
||||
|
||||
/// file构造函数
|
||||
/// @param uploadSource 文件源
|
||||
/// @param key 上传key
|
||||
/// @param token 上传token
|
||||
/// @param option 上传option
|
||||
/// @param config 上传config
|
||||
/// @param recorder 断点续传记录信息
|
||||
/// @param recorderKey 断电上传信息保存的key值,需确保唯一性
|
||||
/// @param completionHandler 上传完成回调
|
||||
- (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
recorder:(id<QNRecorderDelegate>)recorder
|
||||
recorderKey:(NSString *)recorderKey
|
||||
completionHandler:(QNUpTaskCompletionHandler)completionHandler;
|
||||
|
||||
/// data 构造函数
|
||||
/// @param data 上传data流
|
||||
/// @param key 上传key
|
||||
/// @param fileName 上传fileName
|
||||
/// @param token 上传token
|
||||
/// @param option 上传option
|
||||
/// @param config 上传config
|
||||
/// @param completionHandler 上传完成回调
|
||||
- (instancetype)initWithData:(NSData *)data
|
||||
key:(NSString *)key
|
||||
fileName:(NSString *)fileName
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
completionHandler:(QNUpTaskCompletionHandler)completionHandler;
|
||||
|
||||
/// 初始化数据
|
||||
- (void)initData;
|
||||
|
||||
//MARK: -- 上传
|
||||
|
||||
/// 开始上传流程
|
||||
- (void)run;
|
||||
|
||||
/// 准备上传
|
||||
- (int)prepareToUpload;
|
||||
|
||||
/// 重新加载上传数据
|
||||
- (BOOL)reloadUploadInfo;
|
||||
|
||||
/// 开始上传
|
||||
- (void)startToUpload;
|
||||
|
||||
/// 切换区域
|
||||
- (BOOL)switchRegionAndUpload;
|
||||
// 根据错误信息进行切换region并上传,return:是否切换region并上传
|
||||
- (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo;
|
||||
|
||||
/// 上传结束调用回调方法,在上传结束时调用,该方法内部会调用回调,已通知上层上传结束
|
||||
/// @param info 上传返回信息
|
||||
/// @param response 上传字典信息
|
||||
- (void)complete:(QNResponseInfo *)info
|
||||
response:(NSDictionary *)response;
|
||||
|
||||
//MARK: -- 机房管理
|
||||
|
||||
/// 在区域列表头部插入一个区域
|
||||
- (void)insertRegionAtFirst:(id <QNUploadRegion>)region;
|
||||
/// 切换区域
|
||||
- (BOOL)switchRegion;
|
||||
/// 获取目标区域
|
||||
- (id <QNUploadRegion>)getTargetRegion;
|
||||
/// 获取当前区域
|
||||
- (id <QNUploadRegion>)getCurrentRegion;
|
||||
|
||||
//MARK: -- upLog
|
||||
|
||||
// 一个上传流程可能会发起多个上传操作(如:上传多个分片),每个上传操作均是以一个Region的host做重试操作
|
||||
- (void)addRegionRequestMetricsOfOneFlow:(QNUploadRegionRequestMetrics *)metrics;
|
||||
|
||||
@end
|
||||
295
Pods/Qiniu/QiniuSDK/Storage/QNBaseUpload.m
generated
Normal file
295
Pods/Qiniu/QiniuSDK/Storage/QNBaseUpload.m
generated
Normal file
@@ -0,0 +1,295 @@
|
||||
//
|
||||
// QNBaseUpload.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2020/4/19.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNAutoZone.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNDefine.h"
|
||||
#import "QNBaseUpload.h"
|
||||
#import "QNUploadDomainRegion.h"
|
||||
|
||||
NSString *const QNUploadUpTypeForm = @"form";
|
||||
NSString *const QNUploadUpTypeResumableV1 = @"resumable_v1";
|
||||
NSString *const QNUploadUpTypeResumableV2 = @"resumable_v2";
|
||||
|
||||
@interface QNBaseUpload ()
|
||||
|
||||
@property (nonatomic, strong) QNBaseUpload *strongSelf;
|
||||
|
||||
@property (nonatomic, copy) NSString *key;
|
||||
@property (nonatomic, copy) NSString *fileName;
|
||||
@property (nonatomic, strong) NSData *data;
|
||||
@property (nonatomic, strong) id <QNUploadSource> uploadSource;
|
||||
@property (nonatomic, strong) QNUpToken *token;
|
||||
@property (nonatomic, copy) NSString *identifier;
|
||||
@property (nonatomic, strong) QNUploadOption *option;
|
||||
@property (nonatomic, strong) QNConfiguration *config;
|
||||
@property (nonatomic, strong) id <QNRecorderDelegate> recorder;
|
||||
@property (nonatomic, copy) NSString *recorderKey;
|
||||
@property (nonatomic, strong) QNUpTaskCompletionHandler completionHandler;
|
||||
|
||||
@property (nonatomic, assign)NSInteger currentRegionIndex;
|
||||
@property (nonatomic, strong)NSMutableArray <id <QNUploadRegion> > *regions;
|
||||
|
||||
@property (nonatomic, strong)QNUploadRegionRequestMetrics *currentRegionRequestMetrics;
|
||||
@property (nonatomic, strong) QNUploadTaskMetrics *metrics;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNBaseUpload
|
||||
|
||||
- (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
recorder:(id<QNRecorderDelegate>)recorder
|
||||
recorderKey:(NSString *)recorderKey
|
||||
completionHandler:(QNUpTaskCompletionHandler)completionHandler{
|
||||
return [self initWithSource:uploadSource data:nil fileName:[uploadSource getFileName] key:key token:token option:option configuration:config recorder:recorder recorderKey:recorderKey completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data
|
||||
key:(NSString *)key
|
||||
fileName:(NSString *)fileName
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
completionHandler:(QNUpTaskCompletionHandler)completionHandler{
|
||||
return [self initWithSource:nil data:data fileName:fileName key:key token:token option:option configuration:config recorder:nil recorderKey:nil completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
|
||||
data:(NSData *)data
|
||||
fileName:(NSString *)fileName
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
recorder:(id<QNRecorderDelegate>)recorder
|
||||
recorderKey:(NSString *)recorderKey
|
||||
completionHandler:(QNUpTaskCompletionHandler)completionHandler{
|
||||
if (self = [super init]) {
|
||||
_uploadSource = uploadSource;
|
||||
_data = data;
|
||||
_fileName = fileName ?: @"?";
|
||||
_key = key;
|
||||
_token = token;
|
||||
_config = config;
|
||||
_option = option ?: [QNUploadOption defaultOptions];
|
||||
_recorder = recorder;
|
||||
_recorderKey = recorderKey;
|
||||
_completionHandler = completionHandler;
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initData{
|
||||
_strongSelf = self;
|
||||
_currentRegionIndex = 0;
|
||||
}
|
||||
|
||||
- (void)run {
|
||||
[self.metrics start];
|
||||
|
||||
kQNWeakSelf;
|
||||
[_config.zone query:self.config token:self.token on:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, QNZonesInfo * _Nullable zonesInfo) {
|
||||
|
||||
kQNStrongSelf;
|
||||
self.metrics.ucQueryMetrics = metrics;
|
||||
|
||||
if (responseInfo != nil && responseInfo.isOK && zonesInfo) {
|
||||
if (![self setupRegions:zonesInfo]) {
|
||||
responseInfo = [QNResponseInfo responseInfoWithInvalidArgument:[NSString stringWithFormat:@"setup regions host fail, origin response:%@", responseInfo]];
|
||||
[self complete:responseInfo response:responseInfo.responseDictionary];
|
||||
return;
|
||||
}
|
||||
|
||||
int prepareCode = [self prepareToUpload];
|
||||
if (prepareCode == 0) {
|
||||
[self startToUpload];
|
||||
} else {
|
||||
QNResponseInfo *responseInfoP = [QNResponseInfo errorResponseInfo:prepareCode errorDesc:nil];
|
||||
[self complete:responseInfoP response:responseInfoP.responseDictionary];
|
||||
}
|
||||
} else {
|
||||
if (responseInfo == nil) {
|
||||
// responseInfo 一定会有值
|
||||
responseInfo = [QNResponseInfo responseInfoWithSDKInteriorError:@"can't get regions"];
|
||||
}
|
||||
|
||||
[self complete:responseInfo response:responseInfo.responseDictionary];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)reloadUploadInfo {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (int)prepareToUpload{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)startToUpload{
|
||||
self.currentRegionRequestMetrics = [[QNUploadRegionRequestMetrics alloc] initWithRegion:[self getCurrentRegion]];
|
||||
[self.currentRegionRequestMetrics start];
|
||||
}
|
||||
|
||||
// 内部不再调用
|
||||
- (BOOL)switchRegionAndUpload{
|
||||
if (self.currentRegionRequestMetrics) {
|
||||
[self.currentRegionRequestMetrics end];
|
||||
[self.metrics addMetrics:self.currentRegionRequestMetrics];
|
||||
self.currentRegionRequestMetrics = nil;
|
||||
}
|
||||
|
||||
BOOL isSwitched = [self switchRegion];
|
||||
if (isSwitched) {
|
||||
[self startToUpload];
|
||||
}
|
||||
return isSwitched;
|
||||
}
|
||||
|
||||
// 根据错误信息进行切换region并上传,return:是否切换region并上传
|
||||
- (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo {
|
||||
if (errorResponseInfo.statusCode == 400 && [errorResponseInfo.message containsString:@"incorrect region"]) {
|
||||
[QNAutoZone clearCache];
|
||||
}
|
||||
|
||||
if (!errorResponseInfo || errorResponseInfo.isOK || // 不存在 || 成功 不需要重试
|
||||
![errorResponseInfo couldRetry] || ![self.config allowBackupHost]) { // 不能重试
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self.currentRegionRequestMetrics) {
|
||||
[self.currentRegionRequestMetrics end];
|
||||
[self.metrics addMetrics:self.currentRegionRequestMetrics];
|
||||
self.currentRegionRequestMetrics = nil;
|
||||
}
|
||||
|
||||
// 重新加载上传数据,上传记录 & Resource index 归零
|
||||
if (![self reloadUploadInfo]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 切换区域,当为 context 过期错误不需要切换区域
|
||||
if (!errorResponseInfo.isCtxExpiedError && ![self switchRegion]) {
|
||||
// 非 context 过期错误,但是切换 region 失败
|
||||
return false;
|
||||
}
|
||||
|
||||
[self startToUpload];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)complete:(QNResponseInfo *)info
|
||||
response:(NSDictionary *)response{
|
||||
|
||||
[self.metrics end];
|
||||
[self.currentRegionRequestMetrics end];
|
||||
|
||||
if (self.currentRegionRequestMetrics) {
|
||||
[self.metrics addMetrics:self.currentRegionRequestMetrics];
|
||||
}
|
||||
if (self.completionHandler) {
|
||||
self.completionHandler(info, _key, _metrics, response);
|
||||
}
|
||||
self.strongSelf = nil;
|
||||
}
|
||||
|
||||
//MARK:-- region
|
||||
- (BOOL)setupRegions:(QNZonesInfo *)zonesInfo{
|
||||
if (zonesInfo == nil || zonesInfo.zonesInfo == nil || zonesInfo.zonesInfo.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableArray *defaultRegions = [NSMutableArray array];
|
||||
NSArray *zoneInfos = zonesInfo.zonesInfo;
|
||||
for (QNZoneInfo *zoneInfo in zoneInfos) {
|
||||
QNUploadDomainRegion *region = [[QNUploadDomainRegion alloc] initWithConfig:self.config];
|
||||
[region setupRegionData:zoneInfo];
|
||||
if (region.isValid) {
|
||||
[defaultRegions addObject:region];
|
||||
}
|
||||
}
|
||||
self.regions = defaultRegions;
|
||||
self.metrics.regions = defaultRegions;
|
||||
return defaultRegions.count > 0;
|
||||
}
|
||||
|
||||
- (void)insertRegionAtFirst:(id <QNUploadRegion>)region{
|
||||
BOOL hasRegion = NO;
|
||||
for (id <QNUploadRegion> regionP in self.regions) {
|
||||
if ([regionP.zoneInfo.regionId isEqualToString:region.zoneInfo.regionId]) {
|
||||
hasRegion = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasRegion) {
|
||||
[self.regions insertObject:region atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)switchRegion{
|
||||
BOOL ret = NO;
|
||||
@synchronized (self) {
|
||||
NSInteger regionIndex = _currentRegionIndex + 1;
|
||||
if (regionIndex < self.regions.count) {
|
||||
_currentRegionIndex = regionIndex;
|
||||
ret = YES;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (id <QNUploadRegion>)getTargetRegion{
|
||||
return self.regions.firstObject;
|
||||
}
|
||||
|
||||
- (id <QNUploadRegion>)getCurrentRegion{
|
||||
id <QNUploadRegion> region = nil;
|
||||
@synchronized (self) {
|
||||
if (self.currentRegionIndex < self.regions.count) {
|
||||
region = self.regions[self.currentRegionIndex];
|
||||
}
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
- (void)addRegionRequestMetricsOfOneFlow:(QNUploadRegionRequestMetrics *)metrics{
|
||||
if (metrics == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
if (self.currentRegionRequestMetrics == nil) {
|
||||
self.currentRegionRequestMetrics = metrics;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self.currentRegionRequestMetrics addMetrics:metrics];
|
||||
}
|
||||
|
||||
- (QNUploadTaskMetrics *)metrics {
|
||||
if (_metrics == nil) {
|
||||
_metrics = [QNUploadTaskMetrics taskMetrics:self.upType];
|
||||
}
|
||||
return _metrics;
|
||||
}
|
||||
@end
|
||||
14
Pods/Qiniu/QiniuSDK/Storage/QNConcurrentResumeUpload.h
generated
Normal file
14
Pods/Qiniu/QiniuSDK/Storage/QNConcurrentResumeUpload.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// QNConcurrentResumeUpload.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2019/7/15.
|
||||
// Copyright © 2019 Qiniu. All rights reserved.
|
||||
//
|
||||
/// 并发分片上传
|
||||
|
||||
#import "QNPartsUpload.h"
|
||||
|
||||
@interface QNConcurrentResumeUpload : QNPartsUpload
|
||||
|
||||
@end
|
||||
43
Pods/Qiniu/QiniuSDK/Storage/QNConcurrentResumeUpload.m
generated
Normal file
43
Pods/Qiniu/QiniuSDK/Storage/QNConcurrentResumeUpload.m
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// QNConcurrentResumeUpload.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by WorkSpace_Sun on 2019/7/15.
|
||||
// Copyright © 2019 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNConcurrentResumeUpload.h"
|
||||
|
||||
@interface QNConcurrentResumeUpload()
|
||||
|
||||
@property(nonatomic, strong) dispatch_group_t uploadGroup;
|
||||
@property(nonatomic, strong) dispatch_queue_t uploadQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNConcurrentResumeUpload
|
||||
|
||||
- (int)prepareToUpload{
|
||||
self.uploadGroup = dispatch_group_create();
|
||||
self.uploadQueue = dispatch_queue_create("com.qiniu.concurrentUpload", DISPATCH_QUEUE_SERIAL);
|
||||
return [super prepareToUpload];
|
||||
}
|
||||
|
||||
- (void)uploadRestData:(dispatch_block_t)completeHandler {
|
||||
QNLogInfo(@"key:%@ 并发分片", self.key);
|
||||
|
||||
for (int i = 0; i < self.config.concurrentTaskCount; i++) {
|
||||
dispatch_group_enter(self.uploadGroup);
|
||||
dispatch_group_async(self.uploadGroup, self.uploadQueue, ^{
|
||||
[super performUploadRestData:^{
|
||||
dispatch_group_leave(self.uploadGroup);
|
||||
}];
|
||||
});
|
||||
}
|
||||
dispatch_group_notify(self.uploadGroup, self.uploadQueue, ^{
|
||||
completeHandler();
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
346
Pods/Qiniu/QiniuSDK/Storage/QNConfiguration.h
generated
Normal file
346
Pods/Qiniu/QiniuSDK/Storage/QNConfiguration.h
generated
Normal file
@@ -0,0 +1,346 @@
|
||||
//
|
||||
// QNConfiguration.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/5/21.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import "QNDns.h"
|
||||
|
||||
/**
|
||||
* 断点上传时的分块大小
|
||||
*/
|
||||
extern const UInt32 kQNBlockSize;
|
||||
|
||||
/**
|
||||
* DNS默认缓存时间
|
||||
*/
|
||||
extern const UInt32 kQNDefaultDnsCacheTime;
|
||||
|
||||
/**
|
||||
* 转换为用户需要的url
|
||||
*
|
||||
* @param url 上传url
|
||||
*
|
||||
* @return 根据上传url算出代理url
|
||||
*/
|
||||
typedef NSString * (^QNUrlConvert)(NSString *url);
|
||||
|
||||
typedef NS_ENUM(NSInteger, QNResumeUploadVersion){
|
||||
QNResumeUploadVersionV1, // 分片v1
|
||||
QNResumeUploadVersionV2 // 分片v2
|
||||
};
|
||||
|
||||
@class QNConfigurationBuilder;
|
||||
@class QNZone;
|
||||
@class QNReportConfig;
|
||||
|
||||
/**
|
||||
* Builder block
|
||||
*
|
||||
* @param builder builder实例
|
||||
*/
|
||||
typedef void (^QNConfigurationBuilderBlock)(QNConfigurationBuilder *builder);
|
||||
|
||||
@interface QNConfiguration : NSObject
|
||||
|
||||
/**
|
||||
* 存储区域
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) QNZone *zone;
|
||||
|
||||
/**
|
||||
* 断点上传时的分片大小
|
||||
*/
|
||||
@property (readonly) UInt32 chunkSize;
|
||||
|
||||
/**
|
||||
* 如果大于此值就使用断点上传,否则使用form上传
|
||||
*/
|
||||
@property (readonly) UInt32 putThreshold;
|
||||
|
||||
/**
|
||||
* 上传失败时每个上传域名的重试次数,默认重试1次
|
||||
*/
|
||||
@property (readonly) UInt32 retryMax;
|
||||
|
||||
/**
|
||||
* 重试前等待时长,默认0.5s
|
||||
*/
|
||||
@property (readonly) NSTimeInterval retryInterval;
|
||||
|
||||
/**
|
||||
* 单个请求超时时间 单位 秒
|
||||
* 注:每个文件上传肯能存在多个操作,当每个操作失败时,可能存在多个请求重试。
|
||||
*/
|
||||
@property (readonly) UInt32 timeoutInterval;
|
||||
|
||||
/**
|
||||
* 是否使用 https,默认为 YES
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL useHttps;
|
||||
|
||||
/**
|
||||
* 单个文件是否开启并发分片上传,默认为NO
|
||||
* 单个文件大小大于4M时,会采用分片上传,每个分片会已单独的请求进行上传操作,多个上传操作可以使用并发,
|
||||
* 也可以采用串行,采用并发时,可以设置并发的个数(对concurrentTaskCount进行设置)。
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL useConcurrentResumeUpload;
|
||||
|
||||
/**
|
||||
* 分片上传版本
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) QNResumeUploadVersion resumeUploadVersion;
|
||||
|
||||
/**
|
||||
* 并发分片上传的并发任务个数,在concurrentResumeUpload为YES时有效,默认为3个
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) UInt32 concurrentTaskCount;
|
||||
|
||||
/**
|
||||
* 重试时是否允许使用备用上传域名,默认为YES
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowBackupHost;
|
||||
|
||||
/**
|
||||
* 是否允许使用加速域名,默认为 false
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL accelerateUploading;
|
||||
|
||||
/**
|
||||
* 持久化记录接口,可以实现将记录持久化到文件,数据库等
|
||||
*/
|
||||
@property (nonatomic, readonly) id<QNRecorderDelegate> recorder;
|
||||
|
||||
/**
|
||||
* 为持久化上传记录,根据上传的key以及文件名 生成持久化的记录key
|
||||
*/
|
||||
@property (nonatomic, readonly) QNRecorderKeyGenerator recorderKeyGen;
|
||||
|
||||
/**
|
||||
* 上传请求代理配置信息
|
||||
*/
|
||||
@property (nonatomic, readonly) NSDictionary *proxy;
|
||||
|
||||
/**
|
||||
* 上传URL转换,使url转换为用户需要的url
|
||||
*/
|
||||
@property (nonatomic, readonly) QNUrlConvert converter;
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
+ (instancetype)defaultConfiguration;
|
||||
|
||||
/**
|
||||
* 使用 QNConfigurationBuilder 进行配置
|
||||
* @param block 配置block
|
||||
*/
|
||||
+ (instancetype)build:(QNConfigurationBuilderBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#define kQNGlobalConfiguration [QNGlobalConfiguration shared]
|
||||
@interface QNGlobalConfiguration : NSObject
|
||||
|
||||
/**
|
||||
* 是否开启dns预解析 默认开启
|
||||
*/
|
||||
@property(nonatomic, assign)BOOL isDnsOpen;
|
||||
|
||||
/**
|
||||
* dns 预取失败后 会进行重新预取 dnsRepreHostNum为最多尝试次数
|
||||
*/
|
||||
@property(nonatomic, assign)UInt32 dnsRepreHostNum;
|
||||
|
||||
/**
|
||||
* dns 预取超时,单位:秒 默认:2
|
||||
*/
|
||||
@property(nonatomic, assign)int dnsResolveTimeout;
|
||||
|
||||
/**
|
||||
* dns 预取, ip 默认有效时间 单位:秒 默认:120
|
||||
* 只有在 dns 预取未返回 ttl 时使用
|
||||
*/
|
||||
@property(nonatomic, assign)UInt32 dnsCacheTime;
|
||||
|
||||
/**
|
||||
* dns预取缓存最大有效时间 单位:秒 默认 1800
|
||||
* 当 dns 缓存 ip 过期并未刷新时,只要在 dnsCacheMaxTTL 时间内仍有效。
|
||||
*/
|
||||
@property(nonatomic, assign)UInt32 dnsCacheMaxTTL;
|
||||
|
||||
/**
|
||||
* 自定义DNS解析客户端host
|
||||
*/
|
||||
@property(nonatomic, strong) id <QNDnsDelegate> dns;
|
||||
|
||||
/**
|
||||
* dns解析结果本地缓存路径
|
||||
*/
|
||||
@property(nonatomic, copy, readonly)NSString *dnsCacheDir;
|
||||
|
||||
/**
|
||||
* 是否使用 udp 方式进行 Dns 预取,默认开启
|
||||
*/
|
||||
@property(nonatomic, assign)BOOL udpDnsEnable;
|
||||
|
||||
/**
|
||||
* 使用 udp 进行 Dns 预取时的 server ipv4 数组;当对某个 Host 使用 udp 进行 Dns 预取时,会使用 udpDnsIps 进行并发预取
|
||||
* 当 udpDnsEnable 开启时,使用 udp 进行 Dns 预取方式才会生效
|
||||
* 默认
|
||||
*/
|
||||
@property(nonatomic, copy) NSArray <NSString *> *udpDnsIpv4Servers;
|
||||
|
||||
/**
|
||||
* 使用 udp 进行 Dns 预取时的 server ipv6 数组;当对某个 Host 使用 udp 进行 Dns 预取时,会使用 udpDnsIps 进行并发预取
|
||||
* 当 udpDnsEnable 开启时,使用 udp 进行 Dns 预取方式才会生效
|
||||
* 默认:nil
|
||||
*/
|
||||
@property(nonatomic, copy) NSArray <NSString *> *udpDnsIpv6Servers;
|
||||
|
||||
/**
|
||||
* 是否使用 doh 预取,默认开启
|
||||
*/
|
||||
@property(nonatomic, assign)BOOL dohEnable;
|
||||
|
||||
/**
|
||||
* 使用 doh 预取时的 server 数组;当对某个 Host 使用 Doh 预取时,会使用 dohServers 进行并发预取
|
||||
* 当 dohEnable 开启时,doh 预取才会生效
|
||||
* 注意:如果使用 ip,需保证服务证书与 IP 绑定,避免 sni 问题
|
||||
*/
|
||||
@property(nonatomic, copy) NSArray <NSString *> *dohIpv4Servers;
|
||||
|
||||
/**
|
||||
* 使用 doh 预取时的 server 数组;当对某个 Host 使用 Doh 预取时,会使用 dohServers 进行并发预取
|
||||
* 当 dohEnable 开启时,doh 预取才会生效
|
||||
* 默认:nil
|
||||
* 注意:如果使用 ip,需保证服务证书与 IP 绑定,避免 sni 问题
|
||||
*/
|
||||
@property(nonatomic, copy) NSArray <NSString *> *dohIpv6Servers;
|
||||
|
||||
/**
|
||||
* Host全局冻结时间 单位:秒 默认:60 推荐范围:[30 ~ 120]
|
||||
* 当某个Host的上传失败后并且可能短时间无法恢复,会冻结该Host
|
||||
*/
|
||||
@property(nonatomic, assign)UInt32 globalHostFrozenTime;
|
||||
|
||||
/**
|
||||
* Host局部冻结时间,只会影响当前上传操作 单位:秒 默认:5*60 推荐范围:[60 ~ 10*60]
|
||||
* 当某个Host的上传失败后并且短时间可能会恢复,会局部冻结该Host
|
||||
*/
|
||||
@property(nonatomic, assign)UInt32 partialHostFrozenTime;
|
||||
|
||||
/**
|
||||
* 网络连接状态检测使用的connectCheckURLStrings,网络链接状态检测可能会影响重试机制,启动网络连接状态检测有助于提高上传可用性。
|
||||
* 当请求的 Response 为网络异常时,并发对 connectCheckURLStrings 中 URLString 进行 HEAD 请求,以此检测当前网络状态的链接状态,其中任意一个 URLString 链接成功则认为当前网络状态链接良好;
|
||||
* 当 connectCheckURLStrings 为 nil 或者 空数组时则弃用检测功能。
|
||||
*/
|
||||
@property(nonatomic, copy)NSArray <NSString *> *connectCheckURLStrings;
|
||||
|
||||
/**
|
||||
* 是否开启网络连接状态检测,默认:开启
|
||||
*/
|
||||
@property(nonatomic, assign)BOOL connectCheckEnable;
|
||||
|
||||
/**
|
||||
* 网络连接状态检测HEAD请求超时,默认:2s
|
||||
*/
|
||||
@property(nonatomic, assign)NSTimeInterval connectCheckTimeout;
|
||||
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNConfigurationBuilder : NSObject
|
||||
|
||||
/**
|
||||
* 默认上传服务器地址
|
||||
*/
|
||||
@property (nonatomic, strong) QNZone *zone;
|
||||
|
||||
/**
|
||||
* 断点上传时的分片大小
|
||||
* 分片 v1 最小为 1024,即 1K,建议用户配置 >= 512K
|
||||
* 分片 v2 最小为 1024 * 1024,即 1M
|
||||
*/
|
||||
@property (assign) UInt32 chunkSize;
|
||||
|
||||
/**
|
||||
* 如果大于此值就使用断点上传,否则使用form上传
|
||||
*/
|
||||
@property (assign) UInt32 putThreshold;
|
||||
|
||||
/**
|
||||
* 上传失败时每个上传域名的重试次数,默认重试1次
|
||||
*/
|
||||
@property (assign) UInt32 retryMax;
|
||||
|
||||
/**
|
||||
* 重试前等待时长,默认0.5s
|
||||
*/
|
||||
@property (assign) NSTimeInterval retryInterval;
|
||||
|
||||
/**
|
||||
* 超时时间 单位 秒
|
||||
*/
|
||||
@property (assign) UInt32 timeoutInterval;
|
||||
|
||||
/**
|
||||
* 是否使用 https,默认为 YES
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL useHttps;
|
||||
|
||||
/**
|
||||
* 重试时是否允许使用备用上传域名,默认为YES
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowBackupHost;
|
||||
|
||||
/**
|
||||
* 是否允许使用加速域名,默认为 false
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL accelerateUploading;
|
||||
|
||||
|
||||
/**
|
||||
* 是否开启并发分片上传,默认为NO
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL useConcurrentResumeUpload;
|
||||
|
||||
/**
|
||||
* 分片上传版本
|
||||
*/
|
||||
@property (nonatomic, assign) QNResumeUploadVersion resumeUploadVersion;
|
||||
|
||||
/**
|
||||
* 并发分片上传的并发任务个数,在concurrentResumeUpload为YES时有效,默认为3个
|
||||
*/
|
||||
@property (nonatomic, assign) UInt32 concurrentTaskCount;
|
||||
|
||||
/**
|
||||
* 持久化记录接口,可以实现将记录持久化到文件,数据库等
|
||||
*/
|
||||
@property (nonatomic, strong) id<QNRecorderDelegate> recorder;
|
||||
|
||||
/**
|
||||
* 为持久化上传记录,根据上传的key以及文件名 生成持久化的记录key
|
||||
*/
|
||||
@property (nonatomic, strong) QNRecorderKeyGenerator recorderKeyGen;
|
||||
|
||||
/**
|
||||
* 上传请求代理配置信息
|
||||
*/
|
||||
@property (nonatomic, strong) NSDictionary *proxy;
|
||||
|
||||
/**
|
||||
* 上传URL转换,使url转换为用户需要的url
|
||||
*/
|
||||
@property (nonatomic, strong) QNUrlConvert converter;
|
||||
|
||||
@end
|
||||
293
Pods/Qiniu/QiniuSDK/Storage/QNConfiguration.m
generated
Normal file
293
Pods/Qiniu/QiniuSDK/Storage/QNConfiguration.m
generated
Normal file
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// QNConfiguration.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/5/21.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNReportConfig.h"
|
||||
#import "QNAutoZone.h"
|
||||
#import "QN_GTM_Base64.h"
|
||||
|
||||
const UInt32 kQNBlockSize = 4 * 1024 * 1024;
|
||||
const UInt32 kQNDefaultDnsCacheTime = 2 * 60;
|
||||
|
||||
|
||||
@implementation QNConfiguration
|
||||
|
||||
+ (instancetype)defaultConfiguration{
|
||||
QNConfigurationBuilder *builder = [[QNConfigurationBuilder alloc] init];
|
||||
return [[QNConfiguration alloc] initWithBuilder:builder];
|
||||
}
|
||||
|
||||
+ (instancetype)build:(QNConfigurationBuilderBlock)block {
|
||||
QNConfigurationBuilder *builder = [[QNConfigurationBuilder alloc] init];
|
||||
block(builder);
|
||||
return [[QNConfiguration alloc] initWithBuilder:builder];
|
||||
}
|
||||
|
||||
- (instancetype)initWithBuilder:(QNConfigurationBuilder *)builder {
|
||||
if (self = [super init]) {
|
||||
_useConcurrentResumeUpload = builder.useConcurrentResumeUpload;
|
||||
_resumeUploadVersion = builder.resumeUploadVersion;
|
||||
_concurrentTaskCount = builder.concurrentTaskCount;
|
||||
|
||||
_chunkSize = builder.chunkSize;
|
||||
if (builder.resumeUploadVersion == QNResumeUploadVersionV1) {
|
||||
if (_chunkSize < 1024) {
|
||||
_chunkSize = 1024;
|
||||
}
|
||||
} else if (builder.resumeUploadVersion == QNResumeUploadVersionV2) {
|
||||
if (_chunkSize < 1024 * 1024) {
|
||||
_chunkSize = 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
_putThreshold = builder.putThreshold;
|
||||
_retryMax = builder.retryMax;
|
||||
_retryInterval = builder.retryInterval;
|
||||
_timeoutInterval = builder.timeoutInterval;
|
||||
|
||||
_recorder = builder.recorder;
|
||||
_recorderKeyGen = builder.recorderKeyGen;
|
||||
|
||||
_proxy = builder.proxy;
|
||||
|
||||
_converter = builder.converter;
|
||||
|
||||
_zone = builder.zone;
|
||||
|
||||
_useHttps = builder.useHttps;
|
||||
|
||||
_allowBackupHost = builder.allowBackupHost;
|
||||
_accelerateUploading = builder.accelerateUploading;
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface QNGlobalConfiguration(){
|
||||
NSArray *_defaultDohIpv4Servers;
|
||||
NSArray *_defaultDohIpv6Servers;
|
||||
NSArray *_defaultUdpDnsIpv4Servers;
|
||||
NSArray *_defaultUdpDnsIpv6Servers;
|
||||
NSArray *_defaultConnectCheckUrls;
|
||||
}
|
||||
|
||||
@property(nonatomic, strong)NSArray *defaultDohIpv4Servers;
|
||||
@property(nonatomic, strong)NSArray *defaultDohIpv6Servers;
|
||||
@property(nonatomic, strong)NSArray *defaultUdpDnsIpv4Servers;
|
||||
@property(nonatomic, strong)NSArray *defaultUdpDnsIpv6Servers;
|
||||
@property(nonatomic, strong)NSArray *defaultConnectCheckUrls;
|
||||
@end
|
||||
@implementation QNGlobalConfiguration
|
||||
+ (instancetype)shared{
|
||||
static QNGlobalConfiguration *config = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
config = [[QNGlobalConfiguration alloc] init];
|
||||
[config setupData];
|
||||
});
|
||||
return config;
|
||||
}
|
||||
- (void)setupData{
|
||||
_isDnsOpen = YES;
|
||||
_dnsResolveTimeout = 2;
|
||||
_dnsCacheDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches/Dns"];
|
||||
_dnsRepreHostNum = 2;
|
||||
_dnsCacheTime = kQNDefaultDnsCacheTime;
|
||||
_dnsCacheMaxTTL = 10*60;
|
||||
|
||||
_dohEnable = true;
|
||||
_defaultDohIpv4Servers = [self parseBase64Array:@"WyJodHRwczovLzIyMy42LjYuNi9kbnMtcXVlcnkiLCAiaHR0cHM6Ly84LjguOC44L2Rucy1xdWVyeSJd"];
|
||||
|
||||
_udpDnsEnable = true;
|
||||
_defaultUdpDnsIpv4Servers = [self parseBase64Array:@"WyIyMjMuNS41LjUiLCAiMTE0LjExNC4xMTQuMTE0IiwgIjEuMS4xLjEiLCAiOC44LjguOCJd"];
|
||||
|
||||
_globalHostFrozenTime = 60;
|
||||
_partialHostFrozenTime = 5*60;
|
||||
|
||||
_connectCheckEnable = YES;
|
||||
_connectCheckTimeout = 2;
|
||||
_defaultConnectCheckUrls = [self parseBase64Array:@"WyJodHRwczovL3d3dy5xaW5pdS5jb20iLCAiaHR0cHM6Ly93d3cuYmFpZHUuY29tIiwgImh0dHBzOi8vd3d3Lmdvb2dsZS5jb20iXQ=="];
|
||||
_connectCheckURLStrings = nil;
|
||||
}
|
||||
|
||||
- (NSArray *)parseBase64Array:(NSString *)data {
|
||||
NSData *jsonData = [QN_GTM_Base64 decodeData:[data dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSArray *ret = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
|
||||
if (ret && [ret isKindOfClass:[NSArray class]]) {
|
||||
return ret;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isDohEnable {
|
||||
return _dohEnable && (self.dohIpv4Servers.count > 0 || self.dohIpv6Servers.count > 0) ;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)dohIpv4Servers {
|
||||
if (_dohIpv4Servers) {
|
||||
return _dohIpv4Servers;
|
||||
} else {
|
||||
return self.defaultDohIpv4Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)dohIpv6Servers {
|
||||
if (_dohIpv6Servers) {
|
||||
return _dohIpv6Servers;
|
||||
} else {
|
||||
return self.defaultDohIpv6Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)udpDnsIpv4Servers {
|
||||
if (_udpDnsIpv4Servers) {
|
||||
return _udpDnsIpv4Servers;
|
||||
} else {
|
||||
return self.defaultUdpDnsIpv4Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)udpDnsIpv6Servers {
|
||||
if (_udpDnsIpv6Servers) {
|
||||
return _udpDnsIpv6Servers;
|
||||
} else {
|
||||
return self.defaultUdpDnsIpv6Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isUdpDnsEnable {
|
||||
return _udpDnsEnable && (self.udpDnsIpv4Servers.count > 0 || self.udpDnsIpv6Servers.count > 0) ;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)connectCheckURLStrings {
|
||||
if (_connectCheckURLStrings) {
|
||||
return _connectCheckURLStrings;
|
||||
} else {
|
||||
return self.defaultConnectCheckUrls;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)defaultDohIpv4Servers {
|
||||
NSArray *arr = nil;
|
||||
@synchronized (self) {
|
||||
if (_defaultDohIpv4Servers) {
|
||||
arr = [_defaultDohIpv4Servers copy];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
- (void)setDefaultDohIpv4Servers:(NSArray *)defaultDohIpv4Servers {
|
||||
@synchronized (self) {
|
||||
_defaultDohIpv4Servers = defaultDohIpv4Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)defaultDohIpv6Servers {
|
||||
NSArray *arr = nil;
|
||||
@synchronized (self) {
|
||||
if (_defaultDohIpv6Servers) {
|
||||
arr = [_defaultDohIpv6Servers copy];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
- (void)setDefaultDohIpv6Servers:(NSArray *)defaultDohIpv6Servers {
|
||||
@synchronized (self) {
|
||||
_defaultDohIpv6Servers = defaultDohIpv6Servers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)defaultUdpDnsIpv4Servers {
|
||||
NSArray *arr = nil;
|
||||
@synchronized (self) {
|
||||
if (_defaultUdpDnsIpv4Servers) {
|
||||
arr = [_defaultUdpDnsIpv4Servers copy];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
- (void)setDefaultUdpDnsIpv4Servers:(NSArray *)defaultUdpDnsIpv4Servers {
|
||||
@synchronized (self) {
|
||||
_defaultUdpDnsIpv4Servers = defaultUdpDnsIpv4Servers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)defaultUdpDnsIpv6Servers {
|
||||
NSArray *arr = nil;
|
||||
@synchronized (self) {
|
||||
if (_defaultUdpDnsIpv6Servers) {
|
||||
arr = [_defaultUdpDnsIpv6Servers copy];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
- (void)setDefaultUdpDnsIpv6Servers:(NSArray *)defaultUdpDnsIpv6Servers {
|
||||
@synchronized (self) {
|
||||
_defaultUdpDnsIpv6Servers = defaultUdpDnsIpv6Servers;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)defaultConnectCheckUrls {
|
||||
NSArray *arr = nil;
|
||||
@synchronized (self) {
|
||||
if (_defaultConnectCheckUrls) {
|
||||
arr = [_defaultConnectCheckUrls copy];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
- (void)setDefaultConnectCheckUrls:(NSArray *)defaultConnectCheckUrls {
|
||||
@synchronized (self) {
|
||||
_defaultConnectCheckUrls = defaultConnectCheckUrls;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNConfigurationBuilder
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_zone = [[QNAutoZone alloc] init];
|
||||
_chunkSize = 2 * 1024 * 1024;
|
||||
_putThreshold = 4 * 1024 * 1024;
|
||||
_retryMax = 1;
|
||||
_timeoutInterval = 90;
|
||||
_retryInterval = 0.5;
|
||||
|
||||
_recorder = nil;
|
||||
_recorderKeyGen = nil;
|
||||
|
||||
_proxy = nil;
|
||||
_converter = nil;
|
||||
|
||||
_useHttps = YES;
|
||||
_allowBackupHost = YES;
|
||||
_accelerateUploading = NO;
|
||||
_useConcurrentResumeUpload = NO;
|
||||
_resumeUploadVersion = QNResumeUploadVersionV1;
|
||||
_concurrentTaskCount = 3;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
13
Pods/Qiniu/QiniuSDK/Storage/QNFormUpload.h
generated
Executable file
13
Pods/Qiniu/QiniuSDK/Storage/QNFormUpload.h
generated
Executable file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// QNFormUpload.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/1/4.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNBaseUpload.h"
|
||||
|
||||
@interface QNFormUpload : QNBaseUpload
|
||||
|
||||
@end
|
||||
73
Pods/Qiniu/QiniuSDK/Storage/QNFormUpload.m
generated
Normal file
73
Pods/Qiniu/QiniuSDK/Storage/QNFormUpload.m
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// QNFormUpload.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/1/4.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
#import "QNDefine.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNFormUpload.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUpProgress.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
|
||||
@interface QNFormUpload ()
|
||||
|
||||
@property(nonatomic, strong)QNUpProgress *progress;
|
||||
|
||||
@property(nonatomic, strong)QNRequestTransaction *uploadTransaction;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNFormUpload
|
||||
|
||||
- (void)startToUpload {
|
||||
[super startToUpload];
|
||||
|
||||
QNLogInfo(@"key:%@ form上传", self.key);
|
||||
|
||||
self.uploadTransaction = [[QNRequestTransaction alloc] initWithConfig:self.config
|
||||
uploadOption:self.option
|
||||
targetRegion:[self getTargetRegion]
|
||||
currentRegion:[self getCurrentRegion]
|
||||
key:self.key
|
||||
token:self.token];
|
||||
|
||||
kQNWeakSelf;
|
||||
void(^progressHandler)(long long totalBytesWritten, long long totalBytesExpectedToWrite) = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite){
|
||||
kQNStrongSelf;
|
||||
[self.progress progress:self.key uploadBytes:totalBytesWritten totalBytes:totalBytesExpectedToWrite];
|
||||
};
|
||||
|
||||
[self.uploadTransaction uploadFormData:self.data
|
||||
fileName:self.fileName
|
||||
progress:progressHandler
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
[self addRegionRequestMetricsOfOneFlow:metrics];
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) {
|
||||
[self complete:responseInfo response:response];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[self.progress notifyDone:self.key totalBytes:self.data.length];
|
||||
[self complete:responseInfo response:response];
|
||||
}];
|
||||
}
|
||||
|
||||
- (QNUpProgress *)progress {
|
||||
if (_progress == nil) {
|
||||
_progress = [QNUpProgress progress:self.option.progressHandler byteProgress:self.option.byteProgressHandler];
|
||||
}
|
||||
return _progress;
|
||||
}
|
||||
|
||||
- (NSString *)upType {
|
||||
return QNUploadUpTypeForm;
|
||||
}
|
||||
@end
|
||||
24
Pods/Qiniu/QiniuSDK/Storage/QNPartsUpload.h
generated
Normal file
24
Pods/Qiniu/QiniuSDK/Storage/QNPartsUpload.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// QNPartsUpload.h
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/7.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
/// 分片上传,默认为串行
|
||||
|
||||
#import "QNBaseUpload.h"
|
||||
#import "QNUploadInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class QNRequestTransaction;
|
||||
@interface QNPartsUpload : QNBaseUpload
|
||||
|
||||
/// 上传剩余的数据,此方法整合上传流程,上传操作为performUploadRestData,默认串行上传
|
||||
- (void)uploadRestData:(dispatch_block_t)completeHandler;
|
||||
- (void)performUploadRestData:(dispatch_block_t)completeHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
324
Pods/Qiniu/QiniuSDK/Storage/QNPartsUpload.m
generated
Normal file
324
Pods/Qiniu/QiniuSDK/Storage/QNPartsUpload.m
generated
Normal file
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// QNPartsUpload.m
|
||||
// QiniuSDK_Mac
|
||||
//
|
||||
// Created by yangsen on 2020/5/7.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNDefine.h"
|
||||
#import "QNUtils.h"
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNPartsUpload.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNReportItem.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
#import "QNPartsUploadPerformerV1.h"
|
||||
#import "QNPartsUploadPerformerV2.h"
|
||||
|
||||
#define kQNRecordFileInfoKey @"recordFileInfo"
|
||||
#define kQNRecordZoneInfoKey @"recordZoneInfo"
|
||||
|
||||
|
||||
@interface QNPartsUpload()
|
||||
|
||||
@property(nonatomic, strong)QNPartsUploadPerformer *uploadPerformer;
|
||||
|
||||
@property( atomic, strong)QNResponseInfo *uploadDataErrorResponseInfo;
|
||||
@property( atomic, strong)NSDictionary *uploadDataErrorResponse;
|
||||
|
||||
@end
|
||||
@implementation QNPartsUpload
|
||||
|
||||
- (void)initData {
|
||||
[super initData];
|
||||
// 根据文件从本地恢复上传信息,如果没有则重新构建上传信息
|
||||
if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
|
||||
QNLogInfo(@"key:%@ 分片V1", self.key);
|
||||
self.uploadPerformer = [[QNPartsUploadPerformerV1 alloc] initWithSource:self.uploadSource
|
||||
fileName:self.fileName
|
||||
key:self.key
|
||||
token:self.token
|
||||
option:self.option
|
||||
configuration:self.config
|
||||
recorderKey:self.recorderKey];
|
||||
} else {
|
||||
QNLogInfo(@"key:%@ 分片V2", self.key);
|
||||
self.uploadPerformer = [[QNPartsUploadPerformerV2 alloc] initWithSource:self.uploadSource
|
||||
fileName:self.fileName
|
||||
key:self.key
|
||||
token:self.token
|
||||
option:self.option
|
||||
configuration:self.config
|
||||
recorderKey:self.recorderKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isAllUploaded {
|
||||
return [self.uploadPerformer.uploadInfo isAllUploaded];
|
||||
}
|
||||
|
||||
- (void)setErrorResponseInfo:(QNResponseInfo *)responseInfo errorResponse:(NSDictionary *)response{
|
||||
if (!responseInfo) {
|
||||
return;
|
||||
}
|
||||
if (!self.uploadDataErrorResponseInfo || responseInfo.statusCode != kQNSDKInteriorError) {
|
||||
self.uploadDataErrorResponseInfo = responseInfo;
|
||||
self.uploadDataErrorResponse = response ?: responseInfo.responseDictionary;
|
||||
}
|
||||
}
|
||||
|
||||
- (int)prepareToUpload{
|
||||
int code = [super prepareToUpload];
|
||||
if (code != 0) {
|
||||
return code;
|
||||
}
|
||||
|
||||
// 配置当前region
|
||||
if (self.uploadPerformer.currentRegion && self.uploadPerformer.currentRegion.isValid) {
|
||||
// currentRegion有值,为断点续传,将region插入至regionList第一处
|
||||
[self insertRegionAtFirst:self.uploadPerformer.currentRegion];
|
||||
QNLogInfo(@"key:%@ 使用缓存region", self.key);
|
||||
} else {
|
||||
// currentRegion无值 切换region
|
||||
[self.uploadPerformer switchRegion:[self getCurrentRegion]];
|
||||
}
|
||||
QNLogInfo(@"key:%@ region:%@", self.key, self.uploadPerformer.currentRegion.zoneInfo.regionId);
|
||||
|
||||
if (self.uploadSource == nil) {
|
||||
code = kQNLocalIOError;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
- (BOOL)switchRegion{
|
||||
BOOL isSuccess = [super switchRegion];
|
||||
if (isSuccess) {
|
||||
[self.uploadPerformer switchRegion:self.getCurrentRegion];
|
||||
QNLogInfo(@"key:%@ 切换region:%@", self.key , self.uploadPerformer.currentRegion.zoneInfo.regionId);
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
- (BOOL)switchRegionAndUploadIfNeededWithErrorResponse:(QNResponseInfo *)errorResponseInfo {
|
||||
[self reportBlock];
|
||||
return [super switchRegionAndUploadIfNeededWithErrorResponse:errorResponseInfo];
|
||||
}
|
||||
|
||||
- (BOOL)reloadUploadInfo {
|
||||
if (![super reloadUploadInfo]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 重新加载资源
|
||||
return [self.uploadPerformer couldReloadInfo] && [self.uploadPerformer reloadInfo];
|
||||
}
|
||||
|
||||
- (void)startToUpload{
|
||||
[super startToUpload];
|
||||
|
||||
// 重置错误信息
|
||||
self.uploadDataErrorResponseInfo = nil;
|
||||
self.uploadDataErrorResponse = nil;
|
||||
|
||||
|
||||
QNLogInfo(@"key:%@ serverInit", self.key);
|
||||
|
||||
// 1. 启动upload
|
||||
kQNWeakSelf;
|
||||
[self serverInit:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) {
|
||||
[self complete:responseInfo response:response];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QNLogInfo(@"key:%@ uploadRestData", self.key);
|
||||
|
||||
// 2. 上传数据
|
||||
kQNWeakSelf;
|
||||
[self uploadRestData:^{
|
||||
kQNStrongSelf;
|
||||
|
||||
if (![self isAllUploaded]) {
|
||||
if (![self switchRegionAndUploadIfNeededWithErrorResponse:self.uploadDataErrorResponseInfo]) {
|
||||
[self complete:self.uploadDataErrorResponseInfo response:self.uploadDataErrorResponse];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有再读取结束再能知道文件大小,需要检测
|
||||
if ([self.uploadPerformer.uploadInfo getSourceSize] == 0) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoOfZeroData:@"file is empty"];
|
||||
[self complete:responseInfo response:responseInfo.responseDictionary];
|
||||
return;
|
||||
}
|
||||
|
||||
QNLogInfo(@"key:%@ completeUpload errorResponseInfo:%@", self.key, self.uploadDataErrorResponseInfo);
|
||||
|
||||
// 3. 组装文件
|
||||
kQNWeakSelf;
|
||||
[self completeUpload:^(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
if (![self switchRegionAndUploadIfNeededWithErrorResponse:responseInfo]) {
|
||||
[self complete:responseInfo response:response];
|
||||
}
|
||||
return;
|
||||
}
|
||||
[self complete:responseInfo response:response];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)uploadRestData:(dispatch_block_t)completeHandler {
|
||||
QNLogInfo(@"key:%@ 串行分片", self.key);
|
||||
[self performUploadRestData:completeHandler];
|
||||
}
|
||||
|
||||
- (void)performUploadRestData:(dispatch_block_t)completeHandler {
|
||||
if ([self isAllUploaded]) {
|
||||
completeHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
[self uploadNextData:^(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
if (stop || !responseInfo.isOK) {
|
||||
completeHandler();
|
||||
} else {
|
||||
[self performUploadRestData:completeHandler];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
//MARK:-- concurrent upload model API
|
||||
- (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
kQNWeakSelf;
|
||||
void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
|
||||
kQNStrongSelf;
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
[self setErrorResponseInfo:responseInfo errorResponse:response];
|
||||
}
|
||||
[self addRegionRequestMetricsOfOneFlow:metrics];
|
||||
completeHandler(responseInfo, response);
|
||||
};
|
||||
|
||||
[self.uploadPerformer serverInit:completeHandlerP];
|
||||
}
|
||||
|
||||
- (void)uploadNextData:(void(^)(BOOL stop, QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
kQNWeakSelf;
|
||||
void(^completeHandlerP)(BOOL, QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(BOOL stop, QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
|
||||
kQNStrongSelf;
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
[self setErrorResponseInfo:responseInfo errorResponse:response];
|
||||
}
|
||||
[self addRegionRequestMetricsOfOneFlow:metrics];
|
||||
completeHandler(stop, responseInfo, response);
|
||||
};
|
||||
|
||||
[self.uploadPerformer uploadNextData:completeHandlerP];
|
||||
}
|
||||
|
||||
- (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo, NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
kQNWeakSelf;
|
||||
void(^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response){
|
||||
kQNStrongSelf;
|
||||
|
||||
if (!responseInfo.isOK) {
|
||||
[self setErrorResponseInfo:responseInfo errorResponse:response];
|
||||
}
|
||||
[self addRegionRequestMetricsOfOneFlow:metrics];
|
||||
completeHandler(responseInfo, response);
|
||||
};
|
||||
[self.uploadPerformer completeUpload:completeHandlerP];
|
||||
}
|
||||
|
||||
|
||||
- (void)complete:(QNResponseInfo *)info response:(NSDictionary *)response{
|
||||
[self.uploadSource close];
|
||||
if ([self shouldRemoveUploadInfoRecord:info]) {
|
||||
[self.uploadPerformer removeUploadInfoRecord];
|
||||
}
|
||||
|
||||
[super complete:info response:response];
|
||||
|
||||
[self reportBlock];
|
||||
}
|
||||
|
||||
- (BOOL)shouldRemoveUploadInfoRecord:(QNResponseInfo *)info {
|
||||
return info.isOK || info.statusCode == 612 || info.statusCode == 614 || info.statusCode == 701;
|
||||
}
|
||||
|
||||
//MARK:-- 统计block日志
|
||||
- (void)reportBlock{
|
||||
|
||||
QNUploadRegionRequestMetrics *metrics = self.currentRegionRequestMetrics ?: [QNUploadRegionRequestMetrics emptyMetrics];
|
||||
|
||||
QNReportItem *item = [QNReportItem item];
|
||||
[item setReportValue:QNReportLogTypeBlock forKey:QNReportBlockKeyLogType];
|
||||
[item setReportValue:@([[NSDate date] timeIntervalSince1970]) forKey:QNReportBlockKeyUpTime];
|
||||
[item setReportValue:self.token.bucket forKey:QNReportBlockKeyTargetBucket];
|
||||
[item setReportValue:self.key forKey:QNReportBlockKeyTargetKey];
|
||||
[item setReportValue:[self getTargetRegion].zoneInfo.regionId forKey:QNReportBlockKeyTargetRegionId];
|
||||
[item setReportValue:[self getCurrentRegion].zoneInfo.regionId forKey:QNReportBlockKeyCurrentRegionId];
|
||||
[item setReportValue:metrics.totalElapsedTime forKey:QNReportBlockKeyTotalElapsedTime];
|
||||
[item setReportValue:metrics.bytesSend forKey:QNReportBlockKeyBytesSent];
|
||||
[item setReportValue:self.uploadPerformer.recoveredFrom forKey:QNReportBlockKeyRecoveredFrom];
|
||||
[item setReportValue:@([self.uploadSource getSize]) forKey:QNReportBlockKeyFileSize];
|
||||
[item setReportValue:@([QNUtils getCurrentProcessID]) forKey:QNReportBlockKeyPid];
|
||||
[item setReportValue:@([QNUtils getCurrentThreadID]) forKey:QNReportBlockKeyTid];
|
||||
|
||||
[item setReportValue:metrics.metricsList.lastObject.hijacked forKey:QNReportBlockKeyHijacking];
|
||||
|
||||
// 统计当前 region 上传速度 文件大小 / 总耗时
|
||||
if (self.uploadDataErrorResponseInfo == nil && [self.uploadSource getSize] > 0 && [metrics totalElapsedTime] > 0) {
|
||||
NSNumber *speed = [QNUtils calculateSpeed:[self.uploadSource getSize] totalTime:[metrics totalElapsedTime].longLongValue];
|
||||
[item setReportValue:speed forKey:QNReportBlockKeyPerceptiveSpeed];
|
||||
}
|
||||
|
||||
if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
|
||||
[item setReportValue:@(1) forKey:QNReportBlockKeyUpApiVersion];
|
||||
} else {
|
||||
[item setReportValue:@(2) forKey:QNReportBlockKeyUpApiVersion];
|
||||
}
|
||||
|
||||
[item setReportValue:[QNUtils getCurrentNetworkType] forKey:QNReportBlockKeyClientTime];
|
||||
[item setReportValue:[QNUtils systemName] forKey:QNReportBlockKeyOsName];
|
||||
[item setReportValue:[QNUtils systemVersion] forKey:QNReportBlockKeyOsVersion];
|
||||
[item setReportValue:[QNUtils sdkLanguage] forKey:QNReportBlockKeySDKName];
|
||||
[item setReportValue:[QNUtils sdkVersion] forKey:QNReportBlockKeySDKVersion];
|
||||
|
||||
[kQNReporter reportItem:item token:self.token.token];
|
||||
}
|
||||
|
||||
- (NSString *)upType {
|
||||
if (self.config == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *sourceType = @"";
|
||||
if ([self.uploadSource respondsToSelector:@selector(sourceType)]) {
|
||||
sourceType = [self.uploadSource sourceType];
|
||||
}
|
||||
if (self.config.resumeUploadVersion == QNResumeUploadVersionV1) {
|
||||
return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV1, sourceType];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"%@<%@>",QNUploadUpTypeResumableV2, sourceType];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
87
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformer.h
generated
Normal file
87
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformer.h
generated
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// QNPartsUploadPerformer.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/12/1.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
/// 抽象类,不可以直接使用,需要使用子类
|
||||
|
||||
#import "QNFileDelegate.h"
|
||||
#import "QNUploadSource.h"
|
||||
#import "QNResponseInfo.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUpToken.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol QNUploadRegion;
|
||||
@class QNUploadInfo, QNRequestTransaction, QNUploadRegionRequestMetrics;
|
||||
|
||||
@interface QNPartsUploadPerformer : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *key;
|
||||
@property (nonatomic, copy, readonly) NSString *fileName;
|
||||
@property (nonatomic, strong, readonly) id <QNUploadSource> uploadSource;
|
||||
@property (nonatomic, strong, readonly) QNUpToken *token;
|
||||
|
||||
@property (nonatomic, strong, readonly) QNUploadOption *option;
|
||||
@property (nonatomic, strong, readonly) QNConfiguration *config;
|
||||
@property (nonatomic, strong, readonly) id <QNRecorderDelegate> recorder;
|
||||
@property (nonatomic, copy, readonly) NSString *recorderKey;
|
||||
|
||||
/// 断点续传时,起始上传偏移
|
||||
@property(nonatomic, strong, readonly)NSNumber *recoveredFrom;
|
||||
@property(nonatomic, strong, readonly)id <QNUploadRegion> currentRegion;
|
||||
@property(nonatomic, strong, readonly)QNUploadInfo *uploadInfo;
|
||||
|
||||
- (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
|
||||
fileName:(NSString *)fileName
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
recorderKey:(NSString *)recorderKey;
|
||||
|
||||
// 是否可以重新加载资源
|
||||
- (BOOL)couldReloadInfo;
|
||||
|
||||
// 重新加载资源
|
||||
- (BOOL)reloadInfo;
|
||||
|
||||
- (void)switchRegion:(id <QNUploadRegion>)region;
|
||||
|
||||
/// 通知回调当前进度
|
||||
- (void)notifyProgress:(BOOL)isCompleted;
|
||||
|
||||
/// 分片信息保存本地
|
||||
- (void)recordUploadInfo;
|
||||
/// 分片信息从本地移除
|
||||
- (void)removeUploadInfoRecord;
|
||||
|
||||
/// 根据字典构造分片信息 【子类实现】
|
||||
- (QNUploadInfo *)getFileInfoWithDictionary:(NSDictionary * _Nonnull)fileInfoDictionary;
|
||||
/// 根据配置构造分片信息 【子类实现】
|
||||
- (QNUploadInfo *)getDefaultUploadInfo;
|
||||
|
||||
- (QNRequestTransaction *)createUploadRequestTransaction;
|
||||
- (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction;
|
||||
|
||||
/// 上传前,服务端配置工作 【子类实现】
|
||||
- (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler;
|
||||
/// 上传文件分片 【子类实现】
|
||||
- (void)uploadNextData:(void(^)(BOOL stop,
|
||||
QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler;
|
||||
/// 完成上传,服务端组织文件信息 【子类实现】
|
||||
- (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
237
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformer.m
generated
Normal file
237
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformer.m
generated
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// QNPartsUploadPerformer.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/12/1.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNUpToken.h"
|
||||
#import "QNZoneInfo.h"
|
||||
#import "QNUploadOption.h"
|
||||
#import "QNConfiguration.h"
|
||||
#import "QNUploadInfo.h"
|
||||
#import "QNUploadRegionInfo.h"
|
||||
#import "QNRecorderDelegate.h"
|
||||
#import "QNUploadDomainRegion.h"
|
||||
#import "QNPartsUploadPerformer.h"
|
||||
#import "QNUpProgress.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
|
||||
#define kQNRecordFileInfoKey @"recordFileInfo"
|
||||
#define kQNRecordZoneInfoKey @"recordZoneInfo"
|
||||
|
||||
@interface QNPartsUploadPerformer()
|
||||
|
||||
@property (nonatomic, copy) NSString *key;
|
||||
@property (nonatomic, copy) NSString *fileName;
|
||||
@property (nonatomic, strong) id <QNUploadSource> uploadSource;
|
||||
@property (nonatomic, strong) QNUpToken *token;
|
||||
|
||||
@property (nonatomic, strong) QNUploadOption *option;
|
||||
@property (nonatomic, strong) QNConfiguration *config;
|
||||
@property (nonatomic, strong) id <QNRecorderDelegate> recorder;
|
||||
@property (nonatomic, copy) NSString *recorderKey;
|
||||
|
||||
@property (nonatomic, strong) NSNumber *recoveredFrom;
|
||||
@property (nonatomic, strong) id <QNUploadRegion> targetRegion;
|
||||
@property (nonatomic, strong) id <QNUploadRegion> currentRegion;
|
||||
@property (nonatomic, strong) QNUploadInfo *uploadInfo;
|
||||
|
||||
@property(nonatomic, strong) NSLock *progressLocker;
|
||||
@property(nonatomic, strong) QNUpProgress *progress;
|
||||
@property(nonatomic, strong) NSMutableArray <QNRequestTransaction *> *uploadTransactions;
|
||||
|
||||
@end
|
||||
@implementation QNPartsUploadPerformer
|
||||
|
||||
- (instancetype)initWithSource:(id<QNUploadSource>)uploadSource
|
||||
fileName:(NSString *)fileName
|
||||
key:(NSString *)key
|
||||
token:(QNUpToken *)token
|
||||
option:(QNUploadOption *)option
|
||||
configuration:(QNConfiguration *)config
|
||||
recorderKey:(NSString *)recorderKey {
|
||||
if (self = [super init]) {
|
||||
_uploadSource = uploadSource;
|
||||
_fileName = fileName;
|
||||
_key = key;
|
||||
_token = token;
|
||||
_option = option;
|
||||
_config = config;
|
||||
_recorder = config.recorder;
|
||||
_recorderKey = recorderKey;
|
||||
|
||||
[self initData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initData {
|
||||
self.uploadTransactions = [NSMutableArray array];
|
||||
|
||||
if (!self.uploadInfo) {
|
||||
self.uploadInfo = [self getDefaultUploadInfo];
|
||||
}
|
||||
[self recoverUploadInfoFromRecord];
|
||||
}
|
||||
|
||||
- (BOOL)couldReloadInfo {
|
||||
return [self.uploadInfo couldReloadSource];
|
||||
}
|
||||
|
||||
- (BOOL)reloadInfo {
|
||||
self.recoveredFrom = nil;
|
||||
[self.uploadInfo clearUploadState];
|
||||
return [self.uploadInfo reloadSource];
|
||||
}
|
||||
|
||||
- (void)switchRegion:(id <QNUploadRegion>)region {
|
||||
self.currentRegion = region;
|
||||
if (!self.targetRegion) {
|
||||
self.targetRegion = region;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyProgress:(BOOL)isCompleted {
|
||||
if (self.uploadInfo == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCompleted) {
|
||||
[self.progress notifyDone:self.key totalBytes:[self.uploadInfo getSourceSize]];
|
||||
} else {
|
||||
[self.progress progress:self.key uploadBytes:[self.uploadInfo uploadSize] totalBytes:[self.uploadInfo getSourceSize]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)recordUploadInfo {
|
||||
|
||||
NSString *key = self.recorderKey;
|
||||
if (self.recorder == nil || key == nil || key.length == 0) {
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
NSDictionary *zoneInfo = [self.currentRegion zoneInfo].detailInfo;
|
||||
NSDictionary *uploadInfo = [self.uploadInfo toDictionary];
|
||||
if (zoneInfo && uploadInfo) {
|
||||
NSDictionary *info = @{kQNRecordZoneInfoKey : zoneInfo,
|
||||
kQNRecordFileInfoKey : uploadInfo};
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:info options:NSJSONWritingPrettyPrinted error:nil];
|
||||
if (data) {
|
||||
[self.recorder set:key data:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recordUploadInfo", self.key, self.recorderKey);
|
||||
}
|
||||
|
||||
- (void)removeUploadInfoRecord {
|
||||
|
||||
self.recoveredFrom = nil;
|
||||
[self.uploadInfo clearUploadState];
|
||||
[self.recorder del:self.recorderKey];
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ removeUploadInfoRecord", self.key, self.recorderKey);
|
||||
}
|
||||
|
||||
- (void)recoverUploadInfoFromRecord {
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recorder:%@ recoverUploadInfoFromRecord", self.key, self.recorderKey, self.recorder);
|
||||
|
||||
NSString *key = self.recorderKey;
|
||||
if (self.recorder == nil || key == nil || [key isEqualToString:@""]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *data = [self.recorder get:key];
|
||||
if (data == nil) {
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recoverUploadInfoFromRecord data:nil", self.key, self.recorderKey);
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
|
||||
if (error != nil || ![info isKindOfClass:[NSDictionary class]]) {
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recoverUploadInfoFromRecord json error", self.key, self.recorderKey);
|
||||
[self.recorder del:self.key];
|
||||
return;
|
||||
}
|
||||
|
||||
QNZoneInfo *zoneInfo = [QNZoneInfo zoneInfoFromDictionary:info[kQNRecordZoneInfoKey]];
|
||||
QNUploadInfo *recoverUploadInfo = [self getFileInfoWithDictionary:info[kQNRecordFileInfoKey]];
|
||||
|
||||
if (zoneInfo && self.uploadInfo && [recoverUploadInfo isValid]
|
||||
&& [self.uploadInfo isSameUploadInfo:recoverUploadInfo]) {
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recoverUploadInfoFromRecord valid", self.key, self.recorderKey);
|
||||
|
||||
[recoverUploadInfo checkInfoStateAndUpdate];
|
||||
self.uploadInfo = recoverUploadInfo;
|
||||
|
||||
QNUploadDomainRegion *region = [[QNUploadDomainRegion alloc] init];
|
||||
[region setupRegionData:zoneInfo];
|
||||
self.currentRegion = region;
|
||||
self.targetRegion = region;
|
||||
self.recoveredFrom = @([recoverUploadInfo uploadSize]);
|
||||
} else {
|
||||
QNLogInfo(@"key:%@ recorderKey:%@ recoverUploadInfoFromRecord invalid", self.key, self.recorderKey);
|
||||
|
||||
[self.recorder del:self.key];
|
||||
self.currentRegion = nil;
|
||||
self.targetRegion = nil;
|
||||
self.recoveredFrom = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (QNRequestTransaction *)createUploadRequestTransaction {
|
||||
QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithConfig:self.config
|
||||
uploadOption:self.option
|
||||
targetRegion:self.targetRegion
|
||||
currentRegion:self.currentRegion
|
||||
key:self.key
|
||||
token:self.token];
|
||||
@synchronized (self) {
|
||||
[self.uploadTransactions addObject:transaction];
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
- (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction {
|
||||
if (transaction) {
|
||||
@synchronized (self) {
|
||||
[self.uploadTransactions removeObject:transaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (QNUploadInfo *)getFileInfoWithDictionary:(NSDictionary *)fileInfoDictionary {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (QNUploadInfo *)getDefaultUploadInfo {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)serverInit:(void (^)(QNResponseInfo * _Nullable,
|
||||
QNUploadRegionRequestMetrics * _Nullable,
|
||||
NSDictionary * _Nullable))completeHandler {}
|
||||
|
||||
- (void)uploadNextData:(void (^)(BOOL stop,
|
||||
QNResponseInfo * _Nullable,
|
||||
QNUploadRegionRequestMetrics * _Nullable,
|
||||
NSDictionary * _Nullable))completeHandler {}
|
||||
|
||||
- (void)completeUpload:(void (^)(QNResponseInfo * _Nullable,
|
||||
QNUploadRegionRequestMetrics * _Nullable,
|
||||
NSDictionary * _Nullable))completeHandler {}
|
||||
|
||||
- (QNUpProgress *)progress {
|
||||
[self.progressLocker lock];
|
||||
if (_progress == nil) {
|
||||
_progress = [QNUpProgress progress:self.option.progressHandler byteProgress:self.option.byteProgressHandler];
|
||||
}
|
||||
[self.progressLocker unlock];
|
||||
return _progress;
|
||||
}
|
||||
|
||||
@end
|
||||
19
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV1.h
generated
Normal file
19
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV1.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// QNPartsUploadApiV1.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNPartsUploadPerformer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNPartsUploadPerformerV1 : QNPartsUploadPerformer
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
188
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV1.m
generated
Normal file
188
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV1.m
generated
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// QNPartsUploadApiV1.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNDefine.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
#import "QNUploadInfoV1.h"
|
||||
#import "QNPartsUploadPerformerV1.h"
|
||||
|
||||
@interface QNPartsUploadPerformerV1()
|
||||
@end
|
||||
@implementation QNPartsUploadPerformerV1
|
||||
+ (long long)blockSize{
|
||||
return 4 * 1024 * 1024;
|
||||
}
|
||||
|
||||
- (QNUploadInfo *)getFileInfoWithDictionary:(NSDictionary *)fileInfoDictionary {
|
||||
return [QNUploadInfoV1 info:self.uploadSource dictionary:fileInfoDictionary];
|
||||
}
|
||||
|
||||
- (QNUploadInfo *)getDefaultUploadInfo {
|
||||
return [QNUploadInfoV1 info:self.uploadSource configuration:self.config];
|
||||
}
|
||||
|
||||
- (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo successResponse];
|
||||
completeHandler(responseInfo, nil, nil);
|
||||
}
|
||||
|
||||
- (void)uploadNextData:(void(^)(BOOL stop,
|
||||
QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
QNUploadInfoV1 *uploadInfo = (QNUploadInfoV1 *)self.uploadInfo;
|
||||
|
||||
NSError *error;
|
||||
QNUploadBlock *block = nil;
|
||||
QNUploadData *chunk = nil;
|
||||
@synchronized (self) {
|
||||
block = [uploadInfo nextUploadBlock:&error];
|
||||
chunk = [uploadInfo nextUploadData:block];
|
||||
chunk.state = QNUploadStateUploading;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoWithLocalIOError:[NSString stringWithFormat:@"%@", error]];
|
||||
completeHandler(YES, responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (block == nil || chunk == nil) {
|
||||
QNLogInfo(@"key:%@ no chunk left", self.key);
|
||||
|
||||
QNResponseInfo *responseInfo = nil;
|
||||
if (uploadInfo.getSourceSize == 0) {
|
||||
responseInfo = [QNResponseInfo responseInfoOfZeroData:@"file is empty"];
|
||||
} else {
|
||||
responseInfo = [QNResponseInfo responseInfoWithSDKInteriorError:@"no chunk left"];
|
||||
}
|
||||
completeHandler(YES, responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunk.data == nil) {
|
||||
QNLogInfo(@"key:%@ chunk data is nil", self.key);
|
||||
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoOfZeroData:@"chunk data is nil"];;
|
||||
completeHandler(YES, responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
void (^progress)(long long, long long) = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite){
|
||||
kQNStrongSelf;
|
||||
chunk.uploadSize = totalBytesWritten;
|
||||
[self notifyProgress:false];
|
||||
};
|
||||
|
||||
void (^completeHandlerP)(QNResponseInfo *, QNUploadRegionRequestMetrics *, NSDictionary *) = ^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
|
||||
NSString *blockContext = response[@"ctx"];
|
||||
NSNumber *expiredAt = response[@"expired_at"];
|
||||
if (responseInfo.isOK && blockContext && expiredAt) {
|
||||
block.context = blockContext;
|
||||
block.expiredAt = expiredAt;
|
||||
chunk.state = QNUploadStateComplete;
|
||||
[self recordUploadInfo];
|
||||
[self notifyProgress:false];
|
||||
} else {
|
||||
chunk.state = QNUploadStateWaitToUpload;
|
||||
}
|
||||
completeHandler(NO, responseInfo, metrics, response);
|
||||
};
|
||||
|
||||
if ([uploadInfo isFirstData:chunk]) {
|
||||
QNLogInfo(@"key:%@ makeBlock", self.key);
|
||||
[self makeBlock:block firstChunk:chunk chunkData:chunk.data progress:progress completeHandler:completeHandlerP];
|
||||
} else {
|
||||
QNLogInfo(@"key:%@ uploadChunk", self.key);
|
||||
[self uploadChunk:block chunk:chunk chunkData:chunk.data progress:progress completeHandler:completeHandlerP];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
QNUploadInfoV1 *uploadInfo = (QNUploadInfoV1 *)self.uploadInfo;
|
||||
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction makeFile:[uploadInfo getSourceSize]
|
||||
fileName:self.fileName
|
||||
blockContexts:[uploadInfo allBlocksContexts]
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
if (responseInfo.isOK) {
|
||||
[self notifyProgress:true];
|
||||
}
|
||||
completeHandler(responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)makeBlock:(QNUploadBlock *)block
|
||||
firstChunk:(QNUploadData *)chunk
|
||||
chunkData:(NSData *)chunkData
|
||||
progress:(void(^)(long long totalBytesWritten,
|
||||
long long totalBytesExpectedToWrite))progress
|
||||
completeHandler:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction makeBlock:block.offset
|
||||
blockSize:block.size
|
||||
firstChunkData:chunkData
|
||||
progress:progress
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
|
||||
completeHandler(responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)uploadChunk:(QNUploadBlock *)block
|
||||
chunk:(QNUploadData *)chunk
|
||||
chunkData:(NSData *)chunkData
|
||||
progress:(void(^)(long long totalBytesWritten,
|
||||
long long totalBytesExpectedToWrite))progress
|
||||
completeHandler:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction uploadChunk:block.context
|
||||
blockOffset:block.offset
|
||||
chunkData:chunkData
|
||||
chunkOffset:chunk.offset
|
||||
progress:progress
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
|
||||
completeHandler(responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV2.h
generated
Normal file
17
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV2.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// QNPartsUploadApiV2.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNPartsUploadPerformer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNPartsUploadPerformerV2 : QNPartsUploadPerformer
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
148
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV2.m
generated
Normal file
148
Pods/Qiniu/QiniuSDK/Storage/QNPartsUploadPerformerV2.m
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// QNPartsUploadApiV2.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2020/11/30.
|
||||
// Copyright © 2020 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNLogUtil.h"
|
||||
#import "QNDefine.h"
|
||||
#import "QNRequestTransaction.h"
|
||||
#import "QNUploadInfoV2.h"
|
||||
#import "QNPartsUploadPerformerV2.h"
|
||||
|
||||
@interface QNPartsUploadPerformerV2()
|
||||
@end
|
||||
@implementation QNPartsUploadPerformerV2
|
||||
|
||||
- (QNUploadInfo *)getFileInfoWithDictionary:(NSDictionary *)fileInfoDictionary {
|
||||
return [QNUploadInfoV2 info:self.uploadSource dictionary:fileInfoDictionary];
|
||||
}
|
||||
|
||||
- (QNUploadInfo *)getDefaultUploadInfo {
|
||||
return [QNUploadInfoV2 info:self.uploadSource configuration:self.config];
|
||||
}
|
||||
|
||||
- (void)serverInit:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
QNUploadInfoV2 *uploadInfo = (QNUploadInfoV2 *)self.uploadInfo;
|
||||
if (uploadInfo && [uploadInfo isValid]) {
|
||||
QNLogInfo(@"key:%@ serverInit success", self.key);
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo successResponse];
|
||||
completeHandler(responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction initPart:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
|
||||
NSString *uploadId = response[@"uploadId"];
|
||||
NSNumber *expireAt = response[@"expireAt"];
|
||||
if (responseInfo.isOK && uploadId && expireAt) {
|
||||
uploadInfo.uploadId = uploadId;
|
||||
uploadInfo.expireAt = expireAt;
|
||||
[self recordUploadInfo];
|
||||
}
|
||||
completeHandler(responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)uploadNextData:(void(^)(BOOL stop,
|
||||
QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
QNUploadInfoV2 *uploadInfo = (QNUploadInfoV2 *)self.uploadInfo;
|
||||
|
||||
NSError *error = nil;
|
||||
QNUploadData *data = nil;
|
||||
@synchronized (self) {
|
||||
data = [uploadInfo nextUploadData:&error];
|
||||
data.state = QNUploadStateUploading;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
QNResponseInfo *responseInfo = [QNResponseInfo responseInfoWithLocalIOError:[NSString stringWithFormat:@"%@", error]];
|
||||
completeHandler(YES, responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// 上传完毕
|
||||
if (data == nil) {
|
||||
QNLogInfo(@"key:%@ no data left", self.key);
|
||||
|
||||
QNResponseInfo *responseInfo = nil;
|
||||
if (uploadInfo.getSourceSize == 0) {
|
||||
responseInfo = [QNResponseInfo responseInfoOfZeroData:@"file is empty"];
|
||||
} else {
|
||||
responseInfo = [QNResponseInfo responseInfoWithSDKInteriorError:@"no chunk left"];
|
||||
}
|
||||
completeHandler(YES, responseInfo, nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
kQNWeakSelf;
|
||||
void (^progress)(long long, long long) = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite){
|
||||
kQNStrongSelf;
|
||||
data.uploadSize = totalBytesWritten;
|
||||
[self notifyProgress:false];
|
||||
};
|
||||
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
|
||||
kQNWeakObj(transaction);
|
||||
[transaction uploadPart:uploadInfo.uploadId
|
||||
partIndex:[uploadInfo getPartIndexOfData:data]
|
||||
partData:data.data
|
||||
progress:progress
|
||||
complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
|
||||
NSString *etag = response[@"etag"];
|
||||
NSString *md5 = response[@"md5"];
|
||||
if (responseInfo.isOK && etag && md5) {
|
||||
data.etag = etag;
|
||||
data.state = QNUploadStateComplete;
|
||||
[self recordUploadInfo];
|
||||
[self notifyProgress:false];
|
||||
} else {
|
||||
data.state = QNUploadStateWaitToUpload;
|
||||
}
|
||||
completeHandler(NO, responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)completeUpload:(void(^)(QNResponseInfo * _Nullable responseInfo,
|
||||
QNUploadRegionRequestMetrics * _Nullable metrics,
|
||||
NSDictionary * _Nullable response))completeHandler {
|
||||
|
||||
QNUploadInfoV2 *uploadInfo = (QNUploadInfoV2 *)self.uploadInfo;
|
||||
|
||||
NSArray *partInfoArray = [uploadInfo getPartInfoArray];
|
||||
QNRequestTransaction *transaction = [self createUploadRequestTransaction];
|
||||
|
||||
kQNWeakSelf;
|
||||
kQNWeakObj(transaction);
|
||||
[transaction completeParts:self.fileName uploadId:uploadInfo.uploadId partInfoArray:partInfoArray complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
|
||||
kQNStrongSelf;
|
||||
kQNStrongObj(transaction);
|
||||
if (responseInfo.isOK) {
|
||||
[self notifyProgress:true];
|
||||
}
|
||||
completeHandler(responseInfo, metrics, response);
|
||||
[self destroyUploadRequestTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
23
Pods/Qiniu/QiniuSDK/Storage/QNUpProgress.h
generated
Normal file
23
Pods/Qiniu/QiniuSDK/Storage/QNUpProgress.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// QNUpProgress.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/5/21.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUploadOption.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QNUpProgress : NSObject
|
||||
|
||||
+ (instancetype)progress:(QNUpProgressHandler)progress byteProgress:(QNUpByteProgressHandler)byteProgress;
|
||||
|
||||
- (void)progress:(NSString *)key uploadBytes:(long long)uploadBytes totalBytes:(long long)totalBytes;
|
||||
|
||||
- (void)notifyDone:(NSString *)key totalBytes:(long long)totalBytes;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
87
Pods/Qiniu/QiniuSDK/Storage/QNUpProgress.m
generated
Normal file
87
Pods/Qiniu/QiniuSDK/Storage/QNUpProgress.m
generated
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// QNUpProgress.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by yangsen on 2021/5/21.
|
||||
// Copyright © 2021 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNAsyncRun.h"
|
||||
#import "QNUpProgress.h"
|
||||
|
||||
@interface QNUpProgress()
|
||||
|
||||
@property(nonatomic, assign)long long maxProgressUploadBytes;
|
||||
@property(nonatomic, assign)long long previousUploadBytes;
|
||||
@property(nonatomic, copy)QNUpProgressHandler progress;
|
||||
@property(nonatomic, copy)QNUpByteProgressHandler byteProgress;
|
||||
|
||||
@end
|
||||
@implementation QNUpProgress
|
||||
|
||||
+ (instancetype)progress:(QNUpProgressHandler)progress byteProgress:(QNUpByteProgressHandler)byteProgress {
|
||||
QNUpProgress *upProgress = [[QNUpProgress alloc] init];
|
||||
upProgress.maxProgressUploadBytes = -1;
|
||||
upProgress.previousUploadBytes = 0;
|
||||
upProgress.progress = progress;
|
||||
upProgress.byteProgress = byteProgress;
|
||||
return upProgress;
|
||||
}
|
||||
|
||||
- (void)progress:(NSString *)key uploadBytes:(long long)uploadBytes totalBytes:(long long)totalBytes {
|
||||
if ((self.progress == nil && self.byteProgress == nil) || uploadBytes < 0 || (totalBytes > 0 && uploadBytes > totalBytes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (totalBytes > 0) {
|
||||
@synchronized (self) {
|
||||
if (self.maxProgressUploadBytes < 0) {
|
||||
self.maxProgressUploadBytes = totalBytes * 0.95;
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadBytes > self.maxProgressUploadBytes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@synchronized (self) {
|
||||
if (uploadBytes > self.previousUploadBytes) {
|
||||
self.previousUploadBytes = uploadBytes;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self notify:key uploadBytes:uploadBytes totalBytes:totalBytes];
|
||||
}
|
||||
|
||||
- (void)notifyDone:(NSString *)key totalBytes:(long long)totalBytes {
|
||||
[self notify:key uploadBytes:totalBytes totalBytes:totalBytes];
|
||||
}
|
||||
|
||||
- (void)notify:(NSString *)key uploadBytes:(long long)uploadBytes totalBytes:(long long)totalBytes {
|
||||
if (self.progress == nil && self.byteProgress == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.byteProgress) {
|
||||
QNAsyncRunInMain(^{
|
||||
self.byteProgress(key, uploadBytes, totalBytes);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (totalBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.progress) {
|
||||
QNAsyncRunInMain(^{
|
||||
double notifyPercent = (double) uploadBytes / (double) totalBytes;
|
||||
self.progress(key, notifyPercent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
33
Pods/Qiniu/QiniuSDK/Storage/QNUpToken.h
generated
Normal file
33
Pods/Qiniu/QiniuSDK/Storage/QNUpToken.h
generated
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// QNUpToken.h
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/6/7.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface QNUpToken : NSObject
|
||||
|
||||
+ (instancetype)parse:(NSString *)token;
|
||||
|
||||
@property (assign, nonatomic, readonly) long deadline;
|
||||
@property (copy , nonatomic, readonly) NSString *access;
|
||||
@property (copy , nonatomic, readonly) NSString *bucket;
|
||||
@property (copy , nonatomic, readonly) NSString *token;
|
||||
|
||||
@property (readonly) BOOL isValid;
|
||||
@property (readonly) BOOL hasReturnUrl;
|
||||
|
||||
+ (instancetype)getInvalidToken;
|
||||
|
||||
- (NSString *)index;
|
||||
|
||||
/// 是否在未来 duration 分钟内有效
|
||||
- (BOOL)isValidForDuration:(long)duration;
|
||||
|
||||
/// 在是否在 date 之前有效
|
||||
- (BOOL)isValidBeforeDate:(NSDate *)date;
|
||||
|
||||
@end
|
||||
103
Pods/Qiniu/QiniuSDK/Storage/QNUpToken.m
generated
Normal file
103
Pods/Qiniu/QiniuSDK/Storage/QNUpToken.m
generated
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// QNUpToken.m
|
||||
// QiniuSDK
|
||||
//
|
||||
// Created by bailong on 15/6/7.
|
||||
// Copyright (c) 2015年 Qiniu. All rights reserved.
|
||||
//
|
||||
|
||||
#import "QNUrlSafeBase64.h"
|
||||
#import "QNUpToken.h"
|
||||
|
||||
#define kQNPolicyKeyScope @"scope"
|
||||
#define kQNPolicyKeyDeadline @"deadline"
|
||||
#define kQNPolicyKeyReturnUrl @"returnUrl"
|
||||
@interface QNUpToken ()
|
||||
|
||||
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QNUpToken
|
||||
|
||||
+ (instancetype)getInvalidToken {
|
||||
QNUpToken *token = [[QNUpToken alloc] init];
|
||||
token->_deadline = -1;
|
||||
return token;
|
||||
}
|
||||
|
||||
- (instancetype)init:(NSDictionary *)policy token:(NSString *)token {
|
||||
if (self = [super init]) {
|
||||
_token = token;
|
||||
_access = [self getAccess];
|
||||
_bucket = [self getBucket:policy];
|
||||
_deadline = [policy[kQNPolicyKeyDeadline] longValue];
|
||||
_hasReturnUrl = (policy[kQNPolicyKeyReturnUrl] != nil);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)getAccess {
|
||||
|
||||
NSRange range = [_token rangeOfString:@":" options:NSCaseInsensitiveSearch];
|
||||
return [_token substringToIndex:range.location];
|
||||
}
|
||||
|
||||
- (NSString *)getBucket:(NSDictionary *)info {
|
||||
|
||||
NSString *scope = [info objectForKey:kQNPolicyKeyScope];
|
||||
if (!scope || [scope isKindOfClass:[NSNull class]]) {
|
||||
return @"";
|
||||
}
|
||||
|
||||
NSRange range = [scope rangeOfString:@":"];
|
||||
if (range.location == NSNotFound) {
|
||||
return scope;
|
||||
}
|
||||
return [scope substringToIndex:range.location];
|
||||
}
|
||||
|
||||
+ (instancetype)parse:(NSString *)token {
|
||||
if (token == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSArray *array = [token componentsSeparatedByString:@":"];
|
||||
if (array == nil || array.count != 3) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = [QNUrlSafeBase64 decodeString:array[2]];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSError *tmp = nil;
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
|
||||
if (tmp != nil || dict[kQNPolicyKeyScope] == nil || dict[kQNPolicyKeyDeadline] == nil) {
|
||||
return nil;
|
||||
}
|
||||
return [[QNUpToken alloc] init:dict token:token];
|
||||
}
|
||||
|
||||
- (NSString *)index {
|
||||
return [NSString stringWithFormat:@"%@:%@", _access, _bucket];
|
||||
}
|
||||
|
||||
- (BOOL)isValid {
|
||||
return _access && _access.length > 0 && _bucket && _bucket.length > 0;
|
||||
}
|
||||
|
||||
- (BOOL)isValidForDuration:(long)duration {
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:duration];
|
||||
return [self isValidBeforeDate:date];
|
||||
}
|
||||
|
||||
- (BOOL)isValidBeforeDate:(NSDate *)date {
|
||||
if (date == nil) {
|
||||
return NO;
|
||||
}
|
||||
return [date timeIntervalSince1970] < self.deadline;
|
||||
}
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user