AliyunOSSSDK/OSSModel.m (639 lines of code) (raw):

// // OSSModel.m // oss_ios_sdk // // Created by zhouzhuo on 8/16/15. // Copyright (c) 2015 aliyun.com. All rights reserved. // #import "OSSDefine.h" #import "OSSModel.h" #import "OSSBolts.h" #import "OSSUtil.h" #import "OSSNetworking.h" #import "OSSLog.h" #import "OSSXMLDictionary.h" #import "OSSSignerParams.h" #import "OSSSignerBase.h" #if TARGET_OS_IOS #import <UIKit/UIDevice.h> #import <UIKit/UIApplication.h> #endif #import "OSSAllRequestNeededMessage.h" #import "OSSNetworkingRequestDelegate.h" @implementation NSDictionary (OSS) - (NSString *)base64JsonString { NSError * error; NSData * jsonData = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error]; if (!jsonData) { return @"e30="; // base64("{}"); } else { NSString * jsonStr = [[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]; NSLog(@"callback json - %@", jsonStr); return [[jsonStr dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; } } @end @implementation OSSSyncMutableDictionary - (instancetype)init { if (self = [super init]) { _dictionary = [NSMutableDictionary dictionary]; _dispatchQueue = dispatch_queue_create("com.aliyun.aliyunsycmutabledictionary", DISPATCH_QUEUE_SERIAL); } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)addObserverForResetCurrentRetryCount { #if TARGET_OS_IOS NSString *notificationName = UIApplicationWillEnterForegroundNotification; // When App switches to active status, refresh the IPv6-only check. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:self selector:@selector(appWillEnterForeground) name:notificationName object:nil]; #endif } - (NSArray *)allKeys { __block NSArray *allKeys = nil; dispatch_sync(self.dispatchQueue, ^{ allKeys = [self.dictionary allKeys]; }); return allKeys; } - (id)objectForKey:(id)aKey { __block id returnObject = nil; dispatch_sync(self.dispatchQueue, ^{ returnObject = [self.dictionary objectForKey:aKey]; }); return returnObject; } - (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey { dispatch_sync(self.dispatchQueue, ^{ [self.dictionary oss_setObject:anObject forKey:aKey]; }); } - (void)removeObjectForKey:(id)aKey { dispatch_sync(self.dispatchQueue, ^{ [self.dictionary removeObjectForKey:aKey]; }); } - (void)appWillEnterForeground { dispatch_sync(self.dispatchQueue, ^{ for (NSString *key in self.dictionary.allKeys) { OSSNetworkingRequestDelegate *delegate = self.dictionary[key]; delegate.currentRetryCount = 0; } }); } @end @implementation OSSFederationToken - (NSString *)description { return [NSString stringWithFormat:@"OSSFederationToken<%p>:{AccessKeyId: %@\nAccessKeySecret: %@\nSecurityToken: %@\nExpiration: %@}", self, _tAccessKey, _tSecretKey, _tToken, _expirationTimeInGMTFormat]; } - (BOOL)useSecurityToken { return self.tToken != nil; } @end @implementation OSSPlainTextAKSKPairCredentialProvider - (instancetype)initWithPlainTextAccessKey:(nonnull NSString *)accessKey secretKey:(nonnull NSString *)secretKey { if (self = [super init]) { self.accessKey = [accessKey oss_trim]; self.secretKey = [secretKey oss_trim]; } return self; } - (nullable NSString *)sign:(NSString *)content error:(NSError **)error { if (![self.accessKey oss_isNotEmpty] || ![self.secretKey oss_isNotEmpty]) { if (error != nil) { *error = [NSError errorWithDomain:OSSClientErrorDomain code:OSSClientErrorCodeSignFailed userInfo:@{OSSErrorMessageTOKEN: @"accessKey or secretKey can't be null"}]; } return nil; } NSString * sign = [OSSUtil calBase64Sha1WithData:content withSecret:self.secretKey]; return [NSString stringWithFormat:@"OSS %@:%@", self.accessKey, sign]; } @end @implementation OSSCustomSignerCredentialProvider - (instancetype)initWithImplementedSigner:(OSSCustomSignContentBlock)signContent { NSParameterAssert(signContent); if (self = [super init]) { _signContent = signContent; } return self; } - (NSString *)sign:(NSString *)content error:(NSError **)error { NSString * signature = @""; @synchronized(self) { signature = self.signContent(content, error); } if (*error) { *error = [NSError errorWithDomain:OSSClientErrorDomain code:OSSClientErrorCodeSignFailed userInfo:[[NSDictionary alloc] initWithDictionary:[*error userInfo]]]; return nil; } return signature; } @end @implementation OSSFederationCredentialProvider - (instancetype)initWithFederationTokenGetter:(OSSGetFederationTokenBlock)federationTokenGetter { if (self = [super init]) { self.federationTokenGetter = federationTokenGetter; } return self; } - (nullable OSSFederationToken *)getToken:(NSError **)error { OSSFederationToken * validToken = nil; @synchronized(self) { if (self.cachedToken == nil) { self.cachedToken = self.federationTokenGetter(); } else { if (self.cachedToken.expirationTimeInGMTFormat) { NSDateFormatter * fm = [NSDateFormatter new]; fm.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; [fm setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; self.cachedToken.expirationTimeInMilliSecond = [[fm dateFromString:self.cachedToken.expirationTimeInGMTFormat] timeIntervalSince1970] * 1000; self.cachedToken.expirationTimeInGMTFormat = nil; OSSLogVerbose(@"Transform GMT date to expirationTimeInMilliSecond: %lld", self.cachedToken.expirationTimeInMilliSecond); } NSDate * expirationDate = [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)(self.cachedToken.expirationTimeInMilliSecond / 1000)]; NSTimeInterval interval = [expirationDate timeIntervalSinceDate:[NSDate oss_clockSkewFixedDate]]; /* if this token will be expired after less than 5 min, we abort it in case of when request arrived oss server, it's expired already. */ if (interval < 5 * 60) { OSSLogDebug(@"get federation token, but after %lf second it would be expired", interval); self.cachedToken = self.federationTokenGetter(); } } validToken = self.cachedToken; } if (!validToken) { if (error != nil) { *error = [NSError errorWithDomain:OSSClientErrorDomain code:OSSClientErrorCodeSignFailed userInfo:@{OSSErrorMessageTOKEN: @"Can't get a federation token"}]; } return nil; } return validToken; } @end @implementation OSSStsTokenCredentialProvider - (OSSFederationToken *)getToken { OSSFederationToken * token = [OSSFederationToken new]; token.tAccessKey = self.accessKeyId; token.tSecretKey = self.secretKeyId; token.tToken = self.securityToken; token.expirationTimeInMilliSecond = NSIntegerMax; return token; } - (instancetype)initWithAccessKeyId:(NSString *)accessKeyId secretKeyId:(NSString *)secretKeyId securityToken:(NSString *)securityToken { if (self = [super init]) { self.accessKeyId = [accessKeyId oss_trim]; self.secretKeyId = [secretKeyId oss_trim]; self.securityToken = [securityToken oss_trim]; } return self; } - (NSString *)sign:(NSString *)content error:(NSError **)error { NSString * sign = [OSSUtil calBase64Sha1WithData:content withSecret:self.secretKeyId]; return [NSString stringWithFormat:@"OSS %@:%@", self.accessKeyId, sign]; } @end @implementation OSSAuthCredentialProvider - (instancetype)initWithAuthServerUrl:(NSString *)authServerUrl { return [self initWithAuthServerUrl:authServerUrl responseDecoder:nil]; } - (instancetype)initWithAuthServerUrl:(NSString *)authServerUrl responseDecoder:(nullable OSSResponseDecoderBlock)decoder { self = [super initWithFederationTokenGetter:^OSSFederationToken * { NSURL * url = [NSURL URLWithString:self.authServerUrl]; NSURLRequest * request = [NSURLRequest requestWithURL:url]; OSSTaskCompletionSource * tcs = [OSSTaskCompletionSource taskCompletionSource]; NSURLSession * session = [NSURLSession sharedSession]; NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { [tcs setError:error]; return; } [tcs setResult:data]; }]; [sessionTask resume]; [tcs.task waitUntilFinished]; if (tcs.task.error) { return nil; } else { NSData* data = tcs.task.result; if(decoder){ data = decoder(data); } NSDictionary * object = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; int statusCode = [[object objectForKey:@"StatusCode"] intValue]; if (statusCode == 200) { OSSFederationToken * token = [OSSFederationToken new]; // All the entries below are mandatory. token.tAccessKey = [object objectForKey:@"AccessKeyId"]; token.tSecretKey = [object objectForKey:@"AccessKeySecret"]; token.tToken = [object objectForKey:@"SecurityToken"]; token.expirationTimeInGMTFormat = [object objectForKey:@"Expiration"]; OSSLogDebug(@"token: %@ %@ %@ %@", token.tAccessKey, token.tSecretKey, token.tToken, [object objectForKey:@"Expiration"]); return token; }else{ return nil; } } }]; if(self){ self.authServerUrl = authServerUrl; } return self; } @end NSString * const BACKGROUND_SESSION_IDENTIFIER = @"com.aliyun.oss.backgroundsession"; @implementation OSSClientConfiguration - (instancetype)init { if (self = [super init]) { self.maxRetryCount = OSSDefaultRetryCount; self.maxConcurrentRequestCount = OSSDefaultMaxConcurrentNum; self.enableBackgroundTransmitService = NO; self.isHttpdnsEnable = NO; self.backgroundSesseionIdentifier = BACKGROUND_SESSION_IDENTIFIER; self.timeoutIntervalForRequest = OSSDefaultTimeoutForRequestInSecond; self.timeoutIntervalForResource = OSSDefaultTimeoutForResourceInSecond; self.isPathStyleAccessEnable = NO; self.isCustomPathPrefixEnable = NO; self.cnameExcludeList = @[]; self.isAllowUACarrySystemInfo = YES; self.isFollowRedirectsEnable = YES; // When the value <= 0, do not set HTTPMaximumConnectionsPerHost and use the default value of NSURLSessionConfiguration self.HTTPMaximumConnectionsPerHost = 0; self.isAllowResetRetryCount = NO; self.isAllowNetworkMetricInfo = NO; self.signVersion = OSSSignVersionV1; } return self; } - (void)setCnameExcludeList:(NSArray *)cnameExcludeList { NSMutableArray * array = [NSMutableArray new]; [cnameExcludeList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSString * host = [(NSString *)obj lowercaseString]; if ([host containsString:@"://"]) { NSString * trimHost = [host substringFromIndex:[host rangeOfString:@"://"].location + 3]; [array addObject:trimHost]; } else { [array addObject:host]; } }]; _cnameExcludeList = array.copy; } @end @implementation OSSSignerInterceptor - (instancetype)initWithCredentialProvider:(id<OSSCredentialProvider>)credentialProvider { if (self = [super init]) { self.credentialProvider = credentialProvider; } return self; } - (OSSTask *)interceptRequestMessage:(OSSAllRequestNeededMessage *)requestMessage { OSSLogVerbose(@"signing intercepting - "); NSString *resource = @"/"; if (requestMessage.bucketName) { resource = [NSString stringWithFormat:@"/%@/", requestMessage.bucketName]; } if (requestMessage.objectKey) { resource = [resource oss_stringByAppendingPathComponentForURL:requestMessage.objectKey]; } OSSSignerParams *signerParams = [[OSSSignerParams alloc] init]; signerParams.region = self.region; signerParams.cloudBoxId = self.cloudBoxId; signerParams.resourcePath = resource; signerParams.credentialProvider = self.credentialProvider; id<OSSRequestSigner> requestSigner = [OSSSignerBase createRequestSignerWithSignerVersion:self.version signerParams:signerParams]; return [requestSigner sign:requestMessage]; } @end @implementation OSSUASettingInterceptor - (instancetype)initWithClientConfiguration:(OSSClientConfiguration *)clientConfiguration{ if (self = [super init]) { self.clientConfiguration = clientConfiguration; } return self; } - (OSSTask *)interceptRequestMessage:(OSSAllRequestNeededMessage *)request { NSString * userAgent = [self getUserAgent:self.clientConfiguration.userAgentMark]; [request.headerParams oss_setObject:userAgent forKey:@"User-Agent"]; return [OSSTask taskWithResult:nil]; } - (NSString *)getUserAgent:(NSString *)customUserAgent { static NSString * userAgent = nil; static dispatch_once_t once; NSString * tempUserAgent = nil; dispatch_once(&once, ^{ NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier]; #if TARGET_OS_IOS if (self.clientConfiguration.isAllowUACarrySystemInfo) { NSString *systemName = [[[UIDevice currentDevice] systemName] stringByReplacingOccurrencesOfString:@" " withString:@"-"]; NSString *systemVersion = [[UIDevice currentDevice] systemVersion]; userAgent = [NSString stringWithFormat:@"%@/%@(/%@/%@/%@)", OSSUAPrefix, OSSSDKVersion, systemName, systemVersion, localeIdentifier]; } else { userAgent = [NSString stringWithFormat:@"%@/%@(/%@)", OSSUAPrefix, OSSSDKVersion, localeIdentifier]; } #elif TARGET_OS_OSX userAgent = [NSString stringWithFormat:@"%@/%@(/%@/%@/%@)", OSSUAPrefix, OSSSDKVersion, @"OSX", [NSProcessInfo processInfo].operatingSystemVersionString, localeIdentifier]; #endif }); if(customUserAgent){ if(userAgent){ tempUserAgent = [[userAgent stringByAppendingString:@"/"] stringByAppendingString:customUserAgent]; }else{ tempUserAgent = customUserAgent; } }else{ tempUserAgent = userAgent; } return tempUserAgent; } @end @implementation OSSTimeSkewedFixingInterceptor - (OSSTask *)interceptRequestMessage:(OSSAllRequestNeededMessage *)request { request.date = [[NSDate oss_clockSkewFixedDate] oss_asStringValue]; return [OSSTask taskWithResult:nil]; } @end @implementation OSSRange - (instancetype)initWithStart:(int64_t)start withEnd:(int64_t)end { if (self = [super init]) { self.startPosition = start; self.endPosition = end; } return self; } - (NSString *)toHeaderString { NSString * rangeString = nil; if (self.startPosition < 0 && self.endPosition < 0) { rangeString = [NSString stringWithFormat:@"bytes=%lld-%lld", self.startPosition, self.endPosition]; } else if (self.startPosition < 0) { rangeString = [NSString stringWithFormat:@"bytes=-%lld", self.endPosition]; } else if (self.endPosition < 0) { rangeString = [NSString stringWithFormat:@"bytes=%lld-", self.startPosition]; } else { rangeString = [NSString stringWithFormat:@"bytes=%lld-%lld", self.startPosition, self.endPosition]; } return rangeString; } @end #pragma mark request and result objects @implementation OSSGetServiceRequest - (NSDictionary *)requestParams { NSMutableDictionary * params = [NSMutableDictionary dictionary]; [params oss_setObject:self.prefix forKey:@"prefix"]; [params oss_setObject:self.marker forKey:@"marker"]; if (self.maxKeys > 0) { [params oss_setObject:[@(self.maxKeys) stringValue] forKey:@"max-keys"]; } return [params copy]; } @end @implementation OSSGetServiceResult @end @implementation OSSCreateBucketRequest - (instancetype)init { self = [super init]; if (self) { _storageClass = OSSBucketStorageClassStandard; } return self; } - (NSString *)storageClassAsString { NSString *storageClassString = nil; switch (_storageClass) { case OSSBucketStorageClassStandard: storageClassString = @"Standard"; break; case OSSBucketStorageClassIA: storageClassString = @"IA"; break; case OSSBucketStorageClassArchive: storageClassString = @"Archive"; break; default: storageClassString = @"Unknown"; break; } return storageClassString; } @end @implementation OSSCreateBucketResult @end @implementation OSSDeleteBucketRequest @end @implementation OSSDeleteBucketResult @end @implementation OSSGetBucketRequest - (NSDictionary *)requestParams { NSMutableDictionary * params = [NSMutableDictionary dictionary]; [params oss_setObject:self.delimiter forKey:@"delimiter"]; [params oss_setObject:self.prefix forKey:@"prefix"]; [params oss_setObject:self.marker forKey:@"marker"]; if (self.maxKeys > 0) { [params oss_setObject:[@(self.maxKeys) stringValue] forKey:@"max-keys"]; } return [params copy]; } @end @implementation OSSListMultipartUploadsRequest - (NSDictionary *)requestParams { NSMutableDictionary * params = [NSMutableDictionary dictionary]; [params oss_setObject:self.delimiter forKey:@"delimiter"]; [params oss_setObject:self.prefix forKey:@"prefix"]; [params oss_setObject:self.keyMarker forKey:@"key-marker"]; [params oss_setObject:self.uploadIdMarker forKey:@"upload-id-marker"]; [params oss_setObject:self.encodingType forKey:@"encoding-type"]; if (self.maxUploads > 0) { [params oss_setObject:[@(self.maxUploads) stringValue] forKey:@"max-uploads"]; } return [params copy]; } @end @implementation OSSListMultipartUploadsResult @end @implementation OSSGetBucketResult @end @implementation OSSGetBucketACLRequest - (NSDictionary *)requestParams { return @{@"acl": @""}; } @end @implementation OSSGetBucketACLResult @end @implementation OSSHeadObjectRequest @end @implementation OSSHeadObjectResult @end @implementation OSSGetObjectRequest @end @implementation OSSGetObjectResult @end @implementation OSSPutObjectACLRequest - (instancetype)init { self = [super init]; if (self) { _acl = @"default"; } return self; } @end @implementation OSSPutObjectACLResult @end @implementation OSSPutObjectRequest - (instancetype)init { if (self = [super init]) { self.objectMeta = [NSDictionary new]; } return self; } @end @implementation OSSPutObjectResult @end @implementation OSSAppendObjectRequest - (instancetype)init { if (self = [super init]) { self.objectMeta = [NSDictionary new]; } return self; } @end @implementation OSSAppendObjectResult @end @implementation OSSDeleteObjectRequest @end @implementation OSSDeleteObjectResult @end @implementation OSSCopyObjectRequest - (instancetype)init { if (self = [super init]) { self.objectMeta = [NSDictionary new]; } return self; } @end @implementation OSSCopyObjectResult @end @implementation OSSInitMultipartUploadRequest - (instancetype)init { if (self = [super init]) { self.objectMeta = [NSDictionary new]; } return self; } @end @implementation OSSInitMultipartUploadResult @end @implementation OSSUploadPartRequest @end @implementation OSSUploadPartResult @end @implementation OSSPartInfo + (instancetype)partInfoWithPartNum:(int32_t)partNum eTag:(NSString *)eTag size:(int64_t)size { return [self partInfoWithPartNum:partNum eTag:eTag size:size crc64:0]; } + (instancetype)partInfoWithPartNum:(int32_t)partNum eTag:(NSString *)eTag size:(int64_t)size crc64:(uint64_t)crc64 { OSSPartInfo *parInfo = [OSSPartInfo new]; parInfo.partNum = partNum; parInfo.eTag = eTag; parInfo.size = size; parInfo.crc64 = crc64; return parInfo; } - (nonnull NSDictionary *)entityToDictionary { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setValue:@(_partNum) forKey:@"partNum"]; if (_eTag) { [dict setValue:_eTag forKey:@"eTag"]; } [dict setValue:@(_size) forKey:@"size"]; [dict setValue:@(_crc64) forKey:@"crc64"]; return [dict copy]; } - (NSString *)description { return [NSString stringWithFormat:@"OSSPartInfo<%p>:{partNum: %d,eTag: %@,partSize: %lld,crc64: %llu}",self,self.partNum,self.eTag,self.size,self.crc64]; } #pragma marks - Protocol Methods - (id)copyWithZone:(nullable NSZone *)zone { OSSPartInfo *instance = [[[self class] allocWithZone:zone] init]; instance.partNum = self.partNum; instance.eTag = self.eTag; instance.size = self.size; instance.crc64 = self.crc64; return instance; } @end @implementation OSSCompleteMultipartUploadRequest @end @implementation OSSCompleteMultipartUploadResult @end @implementation OSSAbortMultipartUploadRequest @end @implementation OSSAbortMultipartUploadResult @end @implementation OSSListPartsRequest @end @implementation OSSListPartsResult @end @implementation OSSMultipartUploadRequest - (instancetype)init { if (self = [super init]) { self.partSize = 256 * 1024; self.threadNum = OSSDefaultThreadNum; self.terminationMode = OSSTerminationModeAll; } return self; } - (void)cancel { [super cancel]; } @end @implementation OSSResumableUploadRequest - (instancetype)init { if (self = [super init]) { self.deleteUploadIdOnCancelling = YES; self.partSize = 256 * 1024; } return self; } - (void)cancel { [super cancel]; if(_runningChildrenRequest){ [_runningChildrenRequest cancel]; } } @end @implementation OSSResumableUploadResult @end @implementation OSSCallBackRequest @end @implementation OSSCallBackResult @end @implementation OSSImagePersistRequest @end @implementation OSSImagePersistResult @end