Changeset View
Changeset View
Standalone View
Standalone View
native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm
#import "CommIOSBlobClient.h" | #import "CommIOSBlobClient.h" | ||||
#import "CommMMKV.h" | |||||
#import "CommSecureStore.h" | #import "CommSecureStore.h" | ||||
#import "Logger.h" | #import "Logger.h" | ||||
#ifdef DEBUG | #ifdef DEBUG | ||||
NSString const *blobServiceAddress = | NSString const *blobServiceAddress = | ||||
@"https://blob.staging.commtechnologies.org"; | @"https://blob.staging.commtechnologies.org"; | ||||
#else | #else | ||||
NSString const *blobServiceAddress = @"https://blob.commtechnologies.org"; | NSString const *blobServiceAddress = @"https://blob.commtechnologies.org"; | ||||
#endif | #endif | ||||
int const blobServiceQueryTimeLimit = 15; | int const blobServiceQueryTimeLimit = 15; | ||||
const std::string mmkvBlobHolderPrefix = "BLOB_HOLDER."; | |||||
// The blob service expects slightly different keys in | |||||
// delete reuqest payload than we use in notif payload | |||||
NSString *const blobServiceHashKey = @"blob_hash"; | |||||
NSString *const blobServiceHolderKey = @"holder"; | |||||
@interface CommIOSBlobClient () | @interface CommIOSBlobClient () | ||||
@property(nonatomic, strong) NSURLSession *sharedBlobServiceSession; | @property(nonatomic, strong) NSURLSession *sharedBlobServiceSession; | ||||
@end | @end | ||||
@implementation CommIOSBlobClient | @implementation CommIOSBlobClient | ||||
+ (id)sharedInstance { | + (id)sharedInstance { | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | dispatch_semaphore_wait( | ||||
(int64_t)(blobServiceQueryTimeLimit * NSEC_PER_SEC))); | (int64_t)(blobServiceQueryTimeLimit * NSEC_PER_SEC))); | ||||
if (requestError) { | if (requestError) { | ||||
*error = requestError; | *error = requestError; | ||||
return nil; | return nil; | ||||
} | } | ||||
return blobContent; | return blobContent; | ||||
} | } | ||||
- (void)deleteBlobAsyncWithHash:(NSString *)blobHash | |||||
andHolder:(NSString *)blobHolder | |||||
withSuccessHandler:(void (^)())successHandler | |||||
andFailureHandler:(void (^)(NSError *))failureHandler { | |||||
NSError *authTokenError = nil; | |||||
NSString *authToken = | |||||
[CommIOSBlobClient _getAuthTokenOrSetError:&authTokenError]; | |||||
if (authTokenError) { | |||||
comm::Logger::log( | |||||
"Failed to create blob service auth token. Reason: " + | |||||
std::string([authTokenError.localizedDescription UTF8String])); | |||||
return; | |||||
} | |||||
NSString *blobUrlStr = [blobServiceAddress stringByAppendingString:@"/blob"]; | |||||
NSURL *blobUrl = [NSURL URLWithString:blobUrlStr]; | |||||
NSMutableURLRequest *deleteRequest = | |||||
[NSMutableURLRequest requestWithURL:blobUrl]; | |||||
[deleteRequest setValue:authToken forHTTPHeaderField:@"Authorization"]; | |||||
[deleteRequest setValue:@"application/json" | |||||
forHTTPHeaderField:@"content-type"]; | |||||
deleteRequest.HTTPMethod = @"DELETE"; | |||||
deleteRequest.HTTPBody = [NSJSONSerialization dataWithJSONObject:@{ | |||||
blobServiceHolderKey : blobHolder, | |||||
blobServiceHashKey : blobHash | |||||
} | |||||
options:0 | |||||
error:nil]; | |||||
NSURLSessionDataTask *task = [self.sharedBlobServiceSession | |||||
dataTaskWithRequest:deleteRequest | |||||
completionHandler:^( | |||||
NSData *_Nullable data, | |||||
NSURLResponse *_Nullable response, | |||||
NSError *_Nullable error) { | |||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | |||||
if (httpResponse.statusCode > 299) { | |||||
NSString *errorMessage = | |||||
[@"Deleting blob failed with the following reason: " | |||||
stringByAppendingString:[NSHTTPURLResponse | |||||
localizedStringForStatusCode: | |||||
httpResponse.statusCode]]; | |||||
failureHandler([NSError | |||||
errorWithDomain:@"app.comm" | |||||
code:httpResponse.statusCode | |||||
userInfo:@{NSLocalizedDescriptionKey : errorMessage}]); | |||||
return; | |||||
} | |||||
if (error) { | |||||
failureHandler(error); | |||||
return; | |||||
} | |||||
successHandler(); | |||||
}]; | |||||
[task resume]; | |||||
} | |||||
- (void)storeBlobForDeletionWithHash:(NSString *)blobHash | |||||
andHolder:(NSString *)blobHolder { | |||||
std::string blobHashCpp = std::string([blobHash UTF8String]); | |||||
std::string blobHolderCpp = std::string([blobHolder UTF8String]); | |||||
std::string mmkvBlobHolderKey = mmkvBlobHolderPrefix + blobHolderCpp; | |||||
comm::CommMMKV::setString(mmkvBlobHolderKey, blobHashCpp); | |||||
} | |||||
- (void)deleteStoredBlobs { | |||||
std::vector<std::string> allKeys = comm::CommMMKV::getAllKeys(); | |||||
NSMutableArray<NSDictionary *> *blobsDataForDeletion = | |||||
[[NSMutableArray alloc] init]; | |||||
for (const auto &key : allKeys) { | |||||
if (key.size() <= mmkvBlobHolderPrefix.size() || | |||||
key.compare(0, mmkvBlobHolderPrefix.size(), mmkvBlobHolderPrefix)) { | |||||
continue; | |||||
} | |||||
std::optional<std::string> blobHash = comm::CommMMKV::getString(key); | |||||
if (!blobHash.has_value()) { | |||||
continue; | |||||
} | |||||
std::string blobHolder = key.substr(mmkvBlobHolderPrefix.size()); | |||||
NSString *blobHolderObjC = | |||||
[NSString stringWithCString:blobHolder.c_str() | |||||
encoding:NSUTF8StringEncoding]; | |||||
NSString *blobHashObjC = | |||||
[NSString stringWithCString:blobHash.value().c_str() | |||||
encoding:NSUTF8StringEncoding]; | |||||
[self deleteBlobAsyncWithHash:blobHashObjC | |||||
andHolder:blobHolderObjC | |||||
withSuccessHandler:^{ | |||||
std::string mmkvBlobHolderKey = mmkvBlobHolderPrefix + blobHolder; | |||||
comm::CommMMKV::removeKeys({mmkvBlobHolderKey}); | |||||
} | |||||
andFailureHandler:^(NSError *error) { | |||||
comm::Logger::log( | |||||
"Failed to delete blob hash " + blobHash.value() + | |||||
" from blob service. Details: " + | |||||
std::string([error.localizedDescription UTF8String])); | |||||
}]; | |||||
} | |||||
} | |||||
- (void)cancelOngoingRequests { | |||||
[self.sharedBlobServiceSession | |||||
getAllTasksWithCompletionHandler:^( | |||||
NSArray<__kindof NSURLSessionTask *> *_Nonnull tasks) { | |||||
for (NSURLSessionTask *task in tasks) { | |||||
[task cancel]; | |||||
} | |||||
}]; | |||||
} | |||||
+ (NSString *)_getAuthTokenOrSetError:(NSError **)error { | + (NSString *)_getAuthTokenOrSetError:(NSError **)error { | ||||
// Authentication data are retrieved on every request | // Authentication data are retrieved on every request | ||||
// since they might change while NSE process is running | // since they might change while NSE process is running | ||||
// so we should not rely on caching them in memory. | // so we should not rely on caching them in memory. | ||||
auto accessToken = comm::CommSecureStore::get( | auto accessToken = comm::CommSecureStore::get( | ||||
comm::CommSecureStore::commServicesAccessToken); | comm::CommSecureStore::commServicesAccessToken); | ||||
auto userID = comm::CommSecureStore::get(comm::CommSecureStore::userID); | auto userID = comm::CommSecureStore::get(comm::CommSecureStore::userID); | ||||
▲ Show 20 Lines • Show All 46 Lines • Show Last 20 Lines |