Files
youle_app_ios/msext/QiniuSDK/Http/QNSessionManager.m
2023-12-27 20:38:37 +08:00

334 lines
13 KiB
Objective-C
Executable File

//
// QNHttpManager.m
// QiniuSDK
//
// Created by bailong on 14/10/1.
// Copyright (c) 2014年 Qiniu. All rights reserved.
//
#import "AFNetworking.h"
#import "HappyDNS.h"
#import "QNAsyncRun.h"
#import "QNConfiguration.h"
#import "QNResponseInfo.h"
#import "QNSessionManager.h"
#include "QNSystem.h"
#import "QNUserAgent.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
@interface QNProgessDelegate : NSObject
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
@property (nonatomic, strong) QNInternalProgressBlock progressBlock;
@property (nonatomic, strong) NSProgress *progress;
@property (nonatomic, strong) NSURLSessionUploadTask *task;
@property (nonatomic, strong) QNCancelBlock cancelBlock;
- (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock;
@end
static NSURL *buildUrl(NSString *host, NSNumber *port, NSString *path) {
port = port == nil ? [NSNumber numberWithInt:80] : port;
NSString *p = [[NSString alloc] initWithFormat:@"http://%@:%@%@", host, port, path];
return [[NSURL alloc] initWithString:p];
}
static BOOL needRetry(NSHTTPURLResponse *httpResponse, NSError *error) {
if (error != nil) {
return error.code < -1000;
}
if (httpResponse == nil) {
return YES;
}
int status = (int)httpResponse.statusCode;
return status >= 500 && status < 600 && status != 579;
}
@implementation QNProgessDelegate
- (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock {
if (self = [super init]) {
_progressBlock = progressBlock;
_progress = nil;
}
return self;
}
- (void)valueChange:(NSProgress *)uploadProgress {
_progressBlock(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
if (_cancelBlock && _cancelBlock()) {
[_task cancel];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
if (context == nil || object == nil) {
return;
}
NSProgress *progress = (NSProgress *)object;
void *p = (__bridge void *)(self);
if (p == context) {
_progressBlock(progress.completedUnitCount, progress.totalUnitCount);
if (_cancelBlock && _cancelBlock()) {
[_task cancel];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end
@interface QNSessionManager ()
@property (nonatomic) AFHTTPSessionManager *httpManager;
@property UInt32 timeout;
@property (nonatomic, strong) QNUrlConvert converter;
@property bool noProxy;
@property (nonatomic) QNDnsManager *dns;
@end
@implementation QNSessionManager
- (instancetype)initWithProxy:(NSDictionary *)proxyDict
timeout:(UInt32)timeout
urlConverter:(QNUrlConvert)converter
dns:(QNDnsManager *)dns {
if (self = [super init]) {
if (proxyDict != nil) {
_noProxy = NO;
} else {
_noProxy = YES;
}
_httpManager = [QNSessionManager httpManagerWithProxy:proxyDict];
_timeout = timeout;
_converter = converter;
_dns = dns;
}
return self;
}
+ (AFHTTPSessionManager *)httpManagerWithProxy:(NSDictionary *)proxyDict {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
if (proxyDict != nil) {
configuration.connectionProxyDictionary = proxyDict;
}
AFHTTPSessionManager *httpManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
httpManager.responseSerializer = [AFHTTPResponseSerializer serializer];
return httpManager;
}
- (instancetype)init {
return [self initWithProxy:nil timeout:60 urlConverter:nil dns:nil];
}
+ (QNResponseInfo *)buildResponseInfo:(NSHTTPURLResponse *)response
withError:(NSError *)error
withDuration:(double)duration
withResponse:(NSData *)body
withHost:(NSString *)host
withIp:(NSString *)ip {
QNResponseInfo *info;
if (response) {
int status = (int)[response statusCode];
NSDictionary *headers = [response allHeaderFields];
NSString *reqId = headers[@"X-Reqid"];
NSString *xlog = headers[@"X-Log"];
NSString *xvia = headers[@"X-Via"];
if (xvia == nil) {
xvia = headers[@"X-Px"];
}
if (xvia == nil) {
xvia = headers[@"Fw-Via"];
}
info = [[QNResponseInfo alloc] init:status withReqId:reqId withXLog:xlog withXVia:xvia withHost:host withIp:ip withDuration:duration withBody:body];
} else {
info = [QNResponseInfo responseInfoWithNetError:error host:host duration:duration];
}
return info;
}
- (void)sendRequest:(NSMutableURLRequest *)request
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
__block NSDate *startTime = [NSDate date];
NSString *domain = request.URL.host;
NSString *u = request.URL.absoluteString;
NSURL *url = request.URL;
NSArray *ips = nil;
if (_converter != nil) {
url = [[NSURL alloc] initWithString:_converter(u)];
request.URL = url;
domain = url.host;
} else if (_noProxy && _dns != nil && [url.scheme isEqualToString:@"http"]) {
if (isIpV6FullySupported() || ![QNIP isV6]) {
ips = [_dns queryWithDomain:[[QNDomain alloc] init:domain hostsFirst:NO hasCname:YES maxTtl:1000]];
double duration = [[NSDate date] timeIntervalSinceDate:startTime];
if (ips == nil || ips.count == 0) {
NSError *error = [[NSError alloc] initWithDomain:domain code:-1003 userInfo:@{ @"error" : @"unkonwn host" }];
QNResponseInfo *info = [QNResponseInfo responseInfoWithNetError:error host:domain duration:duration];
NSLog(@"failure %@", info);
completeBlock(info, nil);
return;
}
}
}
[self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:0 withDomain:domain withRetryTimes:3 withStartTime:startTime withAccess:access];
}
- (void)sendRequest2:(NSMutableURLRequest *)request
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withIpArray:(NSArray *)ips
withIndex:(int)index
withDomain:(NSString *)domain
withRetryTimes:(int)times
withStartTime:(NSDate *)startTime
withAccess:(NSString *)access {
NSURL *url = request.URL;
__block NSString *ip = nil;
if (ips != nil) {
ip = [ips objectAtIndex:(index % ips.count)];
NSString *path = url.path;
if (path == nil || [@"" isEqualToString:path]) {
path = @"/";
}
url = buildUrl(ip, url.port, path);
[request setValue:domain forHTTPHeaderField:@"Host"];
}
request.URL = url;
[request setTimeoutInterval:_timeout];
[request setValue:[[QNUserAgent sharedInstance] getUserAgent:access] forHTTPHeaderField:@"User-Agent"];
[request setValue:nil forHTTPHeaderField:@"Accept-Language"];
if (progressBlock == nil) {
progressBlock = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
};
}
QNInternalProgressBlock progressBlock2 = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
};
__block QNProgessDelegate *delegate = [[QNProgessDelegate alloc] initWithProgress:progressBlock2];
NSURLSessionUploadTask *uploadTask = [_httpManager uploadTaskWithRequest:request fromData:nil progress:^(NSProgress *_Nonnull uploadProgress) {
[delegate valueChange:uploadProgress];
}
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSData *data = responseObject;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
double duration = [[NSDate date] timeIntervalSinceDate:startTime];
QNResponseInfo *info;
NSDictionary *resp = nil;
if (_converter != nil && _noProxy && (index + 1 < ips.count || times > 0) && needRetry(httpResponse, error)) {
[self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:index + 1 withDomain:domain withRetryTimes:times - 1 withStartTime:startTime withAccess:access];
return;
}
if (error == nil) {
info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:duration withResponse:data withHost:domain withIp:ip];
if (info.isOK) {
NSError *tmp;
resp = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
}
} else {
info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:duration withResponse:data withHost:domain withIp:ip];
}
completeBlock(info, resp);
}];
delegate.task = uploadTask;
delegate.cancelBlock = cancelBlock;
[uploadTask resume];
}
- (void)multipartPost:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withFileName:(NSString *)key
withMimeType:(NSString *)mime
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
NSMutableURLRequest *request = [_httpManager.requestSerializer multipartFormRequestWithMethod:@"POST"
URLString:url
parameters:params
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:@"file" fileName:key mimeType:mime];
}
error:nil];
[self sendRequest:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock
withAccess:access];
}
- (void)post:(NSString *)url
withData:(NSData *)data
withParams:(NSDictionary *)params
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock
withProgressBlock:(QNInternalProgressBlock)progressBlock
withCancelBlock:(QNCancelBlock)cancelBlock
withAccess:(NSString *)access {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:url]];
if (headers) {
[request setAllHTTPHeaderFields:headers];
}
[request setHTTPMethod:@"POST"];
if (params) {
[request setValuesForKeysWithDictionary:params];
}
[request setHTTPBody:data];
QNAsyncRun(^{
[self sendRequest:request
withCompleteBlock:completeBlock
withProgressBlock:progressBlock
withCancelBlock:cancelBlock
withAccess:access];
});
}
- (void)get:(NSString *)url
withHeaders:(NSDictionary *)headers
withCompleteBlock:(QNCompleteBlock)completeBlock {
QNAsyncRun(^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSData *s = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *resp = nil;
QNResponseInfo *info;
if (error == nil) {
info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:0 withResponse:s withHost:@"" withIp:@""];
if (info.isOK) {
resp = responseObject;
}
} else {
info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:0 withResponse:s withHost:@"" withIp:@""];
}
completeBlock(info, resp);
}];
[dataTask resume];
});
}
@end
#endif