123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- //
- // OSSNetworking.m
- // oss_ios_sdk
- //
- // Created by zhouzhuo on 8/16/15.
- // Copyright (c) 2015 aliyun.com. All rights reserved.
- //
- #import "OSSDefine.h"
- #import "OSSNetworking.h"
- #import "OSSBolts.h"
- #import "OSSModel.h"
- #import "OSSUtil.h"
- #import "OSSLog.h"
- #import "OSSXMLDictionary.h"
- #import "OSSInputStreamHelper.h"
- #import "OSSNetworkingRequestDelegate.h"
- #import "OSSURLRequestRetryHandler.h"
- #import "OSSHttpResponseParser.h"
- @implementation OSSNetworkingConfiguration
- @end
- @implementation OSSNetworking
- - (instancetype)initWithConfiguration:(OSSNetworkingConfiguration *)configuration {
- if (self = [super init]) {
- self.configuration = configuration;
- NSURLSessionConfiguration * conf = nil;
- NSOperationQueue *delegateQueue = [NSOperationQueue new];
- if (configuration.enableBackgroundTransmitService) {
- conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:self.configuration.backgroundSessionIdentifier];
- } else {
- conf = [NSURLSessionConfiguration defaultSessionConfiguration];
- }
- conf.URLCache = nil;
- if (configuration.timeoutIntervalForRequest > 0) {
- conf.timeoutIntervalForRequest = configuration.timeoutIntervalForRequest;
- }
- if (configuration.timeoutIntervalForResource > 0) {
- conf.timeoutIntervalForResource = configuration.timeoutIntervalForResource;
- }
-
- if (configuration.proxyHost && configuration.proxyPort) {
- // Create an NSURLSessionConfiguration that uses the proxy
- NSDictionary *proxyDict = @{
- @"HTTPEnable" : [NSNumber numberWithInt:1],
- (NSString *)kCFStreamPropertyHTTPProxyHost : configuration.proxyHost,
- (NSString *)kCFStreamPropertyHTTPProxyPort : configuration.proxyPort,
- @"HTTPSEnable" : [NSNumber numberWithInt:1],
- (NSString *)kCFStreamPropertyHTTPSProxyHost : configuration.proxyHost,
- (NSString *)kCFStreamPropertyHTTPSProxyPort : configuration.proxyPort,
- };
- conf.connectionProxyDictionary = proxyDict;
- }
- _session = [NSURLSession sessionWithConfiguration:conf
- delegate:self
- delegateQueue:delegateQueue];
- self.isUsingBackgroundSession = configuration.enableBackgroundTransmitService;
- _sessionDelagateManager = [OSSSyncMutableDictionary new];
- NSOperationQueue * operationQueue = [NSOperationQueue new];
- if (configuration.maxConcurrentRequestCount > 0) {
- operationQueue.maxConcurrentOperationCount = configuration.maxConcurrentRequestCount;
- }
- self.taskExecutor = [OSSExecutor executorWithOperationQueue: operationQueue];
- }
- return self;
- }
- - (OSSTask *)sendRequest:(OSSNetworkingRequestDelegate *)request {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
- OSSLogVerbose(@"NetWorkConnectedMsg : %@",[OSSUtil buildNetWorkConnectedMsg]);
- NSString *operator = [OSSUtil buildOperatorMsg];
- if(operator) OSSLogVerbose(@"Operator : %@",[OSSUtil buildOperatorMsg]);
- });
- OSSLogVerbose(@"send request --------");
- if (self.configuration.proxyHost && self.configuration.proxyPort) {
- request.isAccessViaProxy = YES;
- }
- /* set maximum retry */
- request.retryHandler.maxRetryCount = self.configuration.maxRetryCount;
- OSSTaskCompletionSource * taskCompletionSource = [OSSTaskCompletionSource taskCompletionSource];
- __weak OSSNetworkingRequestDelegate *weakRequest= request;
- request.completionHandler = ^(id responseObject, NSError * error) {
- [weakRequest reset];
-
- // 1.判断是否出错,如果出错的话,直接设置错误信息
- if (error)
- {
- [taskCompletionSource setError:error];
- }else
- {
- [self checkForCrc64WithResult:responseObject
- requestDelegate:weakRequest
- taskCompletionSource:taskCompletionSource];
- }
- };
- [self dataTaskWithDelegate:request];
- return taskCompletionSource.task;
- }
- - (void)checkForCrc64WithResult:(nonnull id)response requestDelegate:(OSSNetworkingRequestDelegate *)delegate taskCompletionSource:(OSSTaskCompletionSource *)source
- {
- OSSResult *result = (OSSResult *)response;
- BOOL hasRange = [delegate.internalRequest valueForHTTPHeaderField:@"Range"] != nil;
- NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:delegate.internalRequest.URL resolvingAgainstBaseURL:YES];
- BOOL hasXOSSProcess = [urlComponents.query containsString:@"x-oss-process"];
- BOOL enableCRC = delegate.crc64Verifiable;
- // 3.判断如果未开启crc校验,或者headerFields里面有Range字段或者参数表中存在
- // x-oss-process字段,都将不进行crc校验
- if (!enableCRC || hasRange || hasXOSSProcess)
- {
- [source setResult:response];
- }
- else
- {
- OSSLogVerbose(@"--- checkForCrc64WithResult --- ");
- // 如果服务端未返回crc信息,默认是成功的
- OSSLogVerbose(@"result.remoteCRC64ecma : %@",result.remoteCRC64ecma);
- OSSLogVerbose(@"if result.localCRC64ecma : %@",result.localCRC64ecma);
- if (!result.remoteCRC64ecma.oss_isNotEmpty)
- {
- [source setResult:response];
- return;
- }
- // getObject 操作的crc数值会在delegate.responseParser consumeHttpResponseBody 进行计算。
- // upload & put 操作在上传成功后再计算。
- // 如果用户设置onReceiveData block。无法计算localCRC64ecma
- if (!result.localCRC64ecma.oss_isNotEmpty)
- {
- OSSLogVerbose(@"delegate.uploadingFileURL : %@",delegate.uploadingFileURL);
- if (delegate.uploadingFileURL)
- {
- OSSInputStreamHelper *helper = [[OSSInputStreamHelper alloc] initWithURL:delegate.uploadingFileURL];
- [helper syncReadBuffers];
- if (helper.crc64 != 0) {
- result.localCRC64ecma = [NSString stringWithFormat:@"%llu",helper.crc64];
- }
- }
- else
- {
- result.localCRC64ecma = delegate.contentCRC;
- }
- OSSLogVerbose(@"finally result.localCRC64ecma : %@",result.localCRC64ecma);
- }
-
- // 针对append接口,需要多次计算crc值
- if ([delegate.lastCRC oss_isNotEmpty] && [result.localCRC64ecma oss_isNotEmpty])
- {
- uint64_t last_crc64,local_crc64;
- NSScanner *scanner = [NSScanner scannerWithString:delegate.lastCRC];
- [scanner scanUnsignedLongLong:&last_crc64];
-
- scanner = [NSScanner scannerWithString:result.localCRC64ecma];
- [scanner scanUnsignedLongLong:&local_crc64];
-
- NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:delegate.internalRequest.URL resolvingAgainstBaseURL:YES];
- NSArray<NSString *> *params = [urlComponents.query componentsSeparatedByString:@"&"];
-
- __block NSString *positionValue;
- [params enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
- if ([obj rangeOfString:@"position="].location == 0)
- {
- *stop = YES;
- positionValue = [obj substringFromIndex:9];
- }
- }];
-
- uint64_t position = [positionValue longLongValue];
- NSString *next_append_position = [result.httpResponseHeaderFields objectForKey:@"x-oss-next-append-position"];
- uint64_t length = [next_append_position longLongValue] - position;
-
- uint64_t crc_local = [OSSUtil crc64ForCombineCRC1:last_crc64 CRC2:local_crc64 length:(size_t)length];
- result.localCRC64ecma = [NSString stringWithFormat:@"%llu",crc_local];
- OSSLogVerbose(@"crc_local: %llu, crc_remote: %@,last_position: %llu,nextAppendPosition: %llu,length: %llu",crc_local,result.remoteCRC64ecma,position,[next_append_position longLongValue],length);
- }
- //如果服务器和本机计算的crc值不一致,则报crc校验失败;否则,认为上传任务执行成功
- if (result.remoteCRC64ecma.oss_isNotEmpty && result.localCRC64ecma.oss_isNotEmpty)
- {
- if ([result.remoteCRC64ecma isEqualToString:result.localCRC64ecma])
- {
- [source setResult:response];
- }else
- {
- NSString *errorMessage = [NSString stringWithFormat:@"crc validation fails(local_crc64ecma: %@,remote_crc64ecma: %@)",result.localCRC64ecma,result.remoteCRC64ecma];
-
- NSError *crcError = [NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeInvalidCRC
- userInfo:@{OSSErrorMessageTOKEN:errorMessage}];
- [source setError:crcError];
- }
- }
- else
- {
- [source setResult:response];
- }
- }
- }
- - (void)dataTaskWithDelegate:(OSSNetworkingRequestDelegate *)requestDelegate {
- [[[[[OSSTask taskWithResult:nil] continueWithExecutor:self.taskExecutor withSuccessBlock:^id(OSSTask *task) {
- OSSLogVerbose(@"start to intercept request");
- for (id<OSSRequestInterceptor> interceptor in requestDelegate.interceptors) {
- task = [interceptor interceptRequestMessage:requestDelegate.allNeededMessage];
- if (task.error) {
- return task;
- }
- }
- return task;
- }] continueWithSuccessBlock:^id(OSSTask *task) {
- return [requestDelegate buildInternalHttpRequest];
- }] continueWithSuccessBlock:^id(OSSTask *task) {
- NSURLSessionDataTask * sessionTask = nil;
- if (self.configuration.timeoutIntervalForRequest > 0) {
- requestDelegate.internalRequest.timeoutInterval = self.configuration.timeoutIntervalForRequest;
- }
- if (requestDelegate.uploadingData) {
- [requestDelegate.internalRequest setHTTPBody:requestDelegate.uploadingData];
- sessionTask = [_session dataTaskWithRequest:requestDelegate.internalRequest];
- } else if (requestDelegate.uploadingFileURL) {
- sessionTask = [_session uploadTaskWithRequest:requestDelegate.internalRequest fromFile:requestDelegate.uploadingFileURL];
- requestDelegate.isBackgroundUploadFileTask = self.isUsingBackgroundSession;
- } else { // not upload request
- sessionTask = [_session dataTaskWithRequest:requestDelegate.internalRequest];
- }
- requestDelegate.currentSessionTask = sessionTask;
- requestDelegate.httpRequestNotSuccessResponseBody = [NSMutableData new];
- [self.sessionDelagateManager setObject:requestDelegate forKey:@(sessionTask.taskIdentifier)];
- if (requestDelegate.isRequestCancelled) {
- return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeTaskCancelled
- userInfo:nil]];
- }
- [sessionTask resume];
-
- return task;
- }] continueWithBlock:^id(OSSTask *task) {
- // if error occurs before created sessionTask
- if (task.error) {
- requestDelegate.completionHandler(nil, task.error);
- } else if (task.isFaulted) {
- requestDelegate.completionHandler(nil, [NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeExcpetionCatched
- userInfo:@{OSSErrorMessageTOKEN: [NSString stringWithFormat:@"Catch exception - %@", task.exception]}]);
- }
- return nil;
- }];
- }
- #pragma mark - NSURLSessionTaskDelegate Methods
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)sessionTask didCompleteWithError:(NSError *)error
- {
- if (error) {
- OSSLogError(@"%@,error: %@", NSStringFromSelector(_cmd), error);
- }
-
- OSSNetworkingRequestDelegate * delegate = [self.sessionDelagateManager objectForKey:@(sessionTask.taskIdentifier)];
- [self.sessionDelagateManager removeObjectForKey:@(sessionTask.taskIdentifier)];
- NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)sessionTask.response;
- if (delegate == nil) {
- OSSLogVerbose(@"delegate: %@", delegate);
- /* if the background transfer service is enable, may recieve the previous task complete callback */
- /* for now, we ignore it */
- return ;
- }
- /* background upload task will not call back didRecieveResponse */
- if (delegate.isBackgroundUploadFileTask) {
- OSSLogVerbose(@"backgroud upload task did recieve response: %@", httpResponse);
- if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 && httpResponse.statusCode != 203) {
- [delegate.responseParser consumeHttpResponse:httpResponse];
- } else {
- delegate.isHttpRequestNotSuccessResponse = YES;
- }
- }
- [[[[OSSTask taskWithResult:nil] continueWithSuccessBlock:^id(OSSTask * task) {
- if (!delegate.error) {
- delegate.error = error;
- }
- if (delegate.error) {
- OSSLogDebug(@"networking request completed with error: %@", error);
- if ([delegate.error.domain isEqualToString:NSURLErrorDomain] && delegate.error.code == NSURLErrorCancelled) {
- return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeTaskCancelled
- userInfo:[error userInfo]]];
- } else {
- NSMutableDictionary * userInfo = [NSMutableDictionary dictionaryWithDictionary:[error userInfo]];
- [userInfo setObject:[NSString stringWithFormat:@"%ld", (long)error.code] forKey:@"OriginErrorCode"];
- return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeNetworkError
- userInfo:userInfo]];
- }
- }
- return task;
- }] continueWithSuccessBlock:^id(OSSTask *task) {
- if (delegate.isHttpRequestNotSuccessResponse) {
- if (httpResponse.statusCode == 0) {
- return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain
- code:OSSClientErrorCodeNetworkingFailWithResponseCode0
- userInfo:@{OSSErrorMessageTOKEN: @"Request failed, response code 0"}]];
- }
- NSString * notSuccessResponseBody = [[NSString alloc] initWithData:delegate.httpRequestNotSuccessResponseBody encoding:NSUTF8StringEncoding];
- OSSLogError(@"http error response: %@", notSuccessResponseBody);
- NSDictionary * dict = [NSDictionary oss_dictionaryWithXMLString:notSuccessResponseBody];
- return [OSSTask taskWithError:[NSError errorWithDomain:OSSServerErrorDomain
- code:(-1 * httpResponse.statusCode)
- userInfo:dict]];
- }
- return task;
- }] continueWithBlock:^id(OSSTask *task) {
- if (task.error) {
-
-
- OSSNetworkingRetryType retryType = [delegate.retryHandler shouldRetry:delegate.currentRetryCount
- requestDelegate:delegate
- response:httpResponse
- error:task.error];
- OSSLogVerbose(@"current retry count: %u, retry type: %d", delegate.currentRetryCount, (int)retryType);
- switch (retryType) {
- case OSSNetworkingRetryTypeShouldNotRetry: {
- delegate.completionHandler(nil, task.error);
- return nil;
- }
- case OSSNetworkingRetryTypeShouldCorrectClockSkewAndRetry: {
- /* correct clock skew */
- NSString * dateStr = [[httpResponse allHeaderFields] objectForKey:@"Date"];
- if ([dateStr length] > 0) {
- NSDate * serverTime = [NSDate oss_dateFromString:dateStr];
- NSDate * deviceTime = [NSDate date];
- NSTimeInterval skewTime = [deviceTime timeIntervalSinceDate:serverTime];
- [NSDate oss_setClockSkew:skewTime];
- } else if (!error) {
- // The response header does not have the 'Date' field.
- // This should not happen.
- OSSLogError(@"Date header does not exist, unable to fix the clock skew");
- }
-
- [delegate.interceptors insertObject:[OSSTimeSkewedFixingInterceptor new] atIndex:0];
- break;
- }
- default:
- break;
- }
- /* now, should retry */
- NSTimeInterval suspendTime = [delegate.retryHandler timeIntervalForRetry:delegate.currentRetryCount retryType:retryType];
- delegate.currentRetryCount++;
- [NSThread sleepForTimeInterval:suspendTime];
-
- if(delegate.retryCallback){
- delegate.retryCallback();
- }
-
- /* retry recursively */
- [delegate reset];
-
- [self dataTaskWithDelegate:delegate];
- } else {
- delegate.completionHandler([delegate.responseParser constructResultObject], nil);
- }
- return nil;
- }];
- }
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
- {
- OSSNetworkingRequestDelegate * delegate = [self.sessionDelagateManager objectForKey:@(task.taskIdentifier)];
- if (delegate.uploadProgress)
- {
- delegate.uploadProgress(bytesSent, totalBytesSent, totalBytesExpectedToSend);
- }
- }
- - (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;
-
- /*
- * Gets the host name
- */
-
- NSString * host = [[task.currentRequest allHTTPHeaderFields] objectForKey:@"Host"];
- if (!host) {
- host = task.currentRequest.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;
- }
- // Uses the default evaluation for other challenges.
- completionHandler(disposition,credential);
- }
- - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
- {
-
- }
- #pragma mark - NSURLSessionDataDelegate Methods
- - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
- {
- /* background upload task will not call back didRecieveResponse */
- OSSLogVerbose(@"%@,response: %@", NSStringFromSelector(_cmd), response);
-
- OSSNetworkingRequestDelegate * delegate = [self.sessionDelagateManager objectForKey:@(dataTask.taskIdentifier)];
- NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
- if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 && httpResponse.statusCode != 203) {
- [delegate.responseParser consumeHttpResponse:httpResponse];
- } else {
- delegate.isHttpRequestNotSuccessResponse = YES;
- }
- completionHandler(NSURLSessionResponseAllow);
- }
- - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
- {
- OSSNetworkingRequestDelegate * delegate = [self.sessionDelagateManager objectForKey:@(dataTask.taskIdentifier)];
- /* background upload task will not call back didRecieveResponse.
- so if we recieve response data after background uploading file,
- we consider it as error response message since a successful uploading request will not response any data */
- if (delegate.isBackgroundUploadFileTask)
- {
- //判断当前的statuscode是否成功
- NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)dataTask.response;
- if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 && httpResponse.statusCode != 203) {
- [delegate.responseParser consumeHttpResponse:httpResponse];
- delegate.isHttpRequestNotSuccessResponse = NO;
- }else
- {
- delegate.isHttpRequestNotSuccessResponse = YES;
- }
- }
-
- if (delegate.isHttpRequestNotSuccessResponse) {
- [delegate.httpRequestNotSuccessResponseBody appendData:data];
- }
- else {
- if (delegate.onRecieveData) {
- delegate.onRecieveData(data);
- } else {
- OSSTask * consumeTask = [delegate.responseParser consumeHttpResponseBody:data];
- if (consumeTask.error) {
- OSSLogError(@"consume data error: %@", consumeTask.error);
- delegate.error = consumeTask.error;
- [dataTask cancel];
- }
- }
- }
- if (!delegate.isHttpRequestNotSuccessResponse && delegate.downloadProgress) {
- int64_t bytesWritten = [data length];
- delegate.payloadTotalBytesWritten += bytesWritten;
- int64_t totalBytesExpectedToWrite = dataTask.response.expectedContentLength;
- delegate.downloadProgress(bytesWritten, delegate.payloadTotalBytesWritten, totalBytesExpectedToWrite);
- }
- }
- #pragma mark - Private Methods
- - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
- /*
- * Creates the policies for certificate verification.
- */
- NSMutableArray *policies = [NSMutableArray array];
- if (domain) {
- [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
- } else {
- [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
- }
-
- /*
- * Sets the policies to server's certificate
- */
- SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
-
-
- /*
- * Evaulates if the current serverTrust is trustable.
- * It's officially suggested that the serverTrust could be passed when result = kSecTrustResultUnspecified or kSecTrustResultProceed.
- * For more information checks out https://developer.apple.com/library/ios/technotes/tn2232/_index.html
- * For detail information about SecTrustResultType, checks out SecTrust.h
- */
- SecTrustResultType result;
- SecTrustEvaluate(serverTrust, &result);
-
- return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
- }
- @end
|