解决了语音上传到问题,接下来要解决下载播放问题
This commit is contained in:
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";
|
||||
Reference in New Issue
Block a user