mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-28 19:05:49 +00:00
175 lines
6.1 KiB
Objective-C
175 lines
6.1 KiB
Objective-C
#import "BITSender.h"
|
|
#import "BITPersistencePrivate.h"
|
|
#import "BITGZIP.h"
|
|
#import "HockeySDKPrivate.h"
|
|
#import "BITHTTPOperation.h"
|
|
#import "BITHockeyHelper.h"
|
|
|
|
static char const *kSenderQueueString = "net.hockeyapp.senderQueue";
|
|
static NSUInteger const defaultRequestLimit = 10;
|
|
|
|
@implementation BITSender
|
|
|
|
@synthesize runningRequestsCount = _runningRequestsCount;
|
|
@synthesize persistence = _persistence;
|
|
|
|
#pragma mark - Initialize instance
|
|
|
|
- (instancetype)initWithPersistence:(BITPersistence *)persistence serverURL:(NSURL *)serverURL {
|
|
if ((self = [super init])) {
|
|
_senderQueue = dispatch_queue_create(kSenderQueueString, DISPATCH_QUEUE_CONCURRENT);
|
|
_maxRequestCount = defaultRequestLimit;
|
|
_serverURL = serverURL;
|
|
_persistence = persistence;
|
|
[self registerObservers];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - Handle persistence events
|
|
|
|
- (void)registerObservers{
|
|
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
__weak typeof(self) weakSelf = self;
|
|
[center addObserverForName:BITPersistenceSuccessNotification
|
|
object:nil
|
|
queue:nil
|
|
usingBlock:^(NSNotification *notification) {
|
|
typeof(self) strongSelf = weakSelf;
|
|
[strongSelf sendSavedDataAsync];
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Sending
|
|
|
|
- (void)sendSavedDataAsync{
|
|
dispatch_async(self.senderQueue, ^{
|
|
[self sendSavedData];
|
|
});
|
|
}
|
|
|
|
- (void)sendSavedData{
|
|
@synchronized(self){
|
|
if(_runningRequestsCount < _maxRequestCount){
|
|
_runningRequestsCount++;
|
|
}else{
|
|
return;
|
|
}
|
|
}
|
|
NSString *path = [self.persistence requestNextPath];
|
|
NSData *data = [self.persistence dataAtPath:path];
|
|
[self sendData:data withPath:path];
|
|
}
|
|
|
|
- (void)sendData:(NSData * __nonnull)data withPath:(NSString * __nonnull)path {
|
|
if(data && data.length > 0) {
|
|
NSData *gzippedData = [data gzippedData];
|
|
NSURLRequest *request = [self requestForData:gzippedData];
|
|
id nsurlsessionClass = NSClassFromString(@"NSURLSessionUploadTask");
|
|
BOOL isUrlSessionSupported = (nsurlsessionClass && !bit_isRunningInAppExtension());
|
|
|
|
[self sendRequest:request path:path urlSessionSupported:isUrlSessionSupported];
|
|
} else {
|
|
self.runningRequestsCount -= 1;
|
|
}
|
|
}
|
|
|
|
- (void)sendRequest:(NSURLRequest * __nonnull)request path:(NSString * __nonnull)path urlSessionSupported:(BOOL)isUrlSessionSupported{
|
|
if(!path || !request) return;
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
if(!isUrlSessionSupported) {
|
|
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
|
|
|
|
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
|
completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
typeof (self) strongSelf = weakSelf;
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
|
NSInteger statusCode = httpResponse.statusCode;
|
|
[strongSelf handleResponseWithStatusCode:statusCode responseData:data filePath:path error:error];
|
|
}];
|
|
[self resumeSessionDataTask:task];
|
|
}else{
|
|
BITHTTPOperation *operation = [BITHTTPOperation operationWithRequest:request];
|
|
[operation setCompletion:^(BITHTTPOperation *operation, NSData *responseData, NSError *error) {
|
|
typeof(self) strongSelf = weakSelf;
|
|
NSInteger statusCode = [operation.response statusCode];
|
|
[strongSelf handleResponseWithStatusCode:statusCode responseData:responseData filePath:path error:error];
|
|
}];
|
|
|
|
[self.operationQueue addOperation:operation];
|
|
}
|
|
}
|
|
|
|
- (void)resumeSessionDataTask:(NSURLSessionDataTask *)sessionDataTask {
|
|
[sessionDataTask resume];
|
|
}
|
|
|
|
- (void)handleResponseWithStatusCode:(NSInteger)statusCode responseData:(NSData *)responseData filePath:(NSString *)filePath error:(NSError *)error{
|
|
self.runningRequestsCount -= 1;
|
|
|
|
if(responseData && [self shouldDeleteDataWithStatusCode:statusCode]) {
|
|
//we delete data that was either sent successfully or if we have a non-recoverable error
|
|
BITHockeyLog(@"Sent data with status code: %ld", (long) statusCode);
|
|
BITHockeyLog(@"Response data:\n%@", [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil]);
|
|
[self.persistence deleteFileAtPath:filePath];
|
|
[self sendSavedData];
|
|
} else {
|
|
BITHockeyLog(@"Sending telemetry data failed");
|
|
BITHockeyLog(@"Error description: %@", error.localizedDescription);
|
|
[self.persistence giveBackRequestedPath:filePath];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Helper
|
|
|
|
- (NSURLRequest *)requestForData:(NSData * __nonnull)data {
|
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
|
|
request.HTTPMethod = @"POST";
|
|
|
|
request.HTTPBody = data;
|
|
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
|
|
|
NSDictionary *headers = @{@"Charset": @"UTF-8",
|
|
@"Content-Encoding": @"gzip",
|
|
@"Content-Type": @"application/x-json-stream",
|
|
@"Accept-Encoding": @"gzip"};
|
|
[request setAllHTTPHeaderFields:headers];
|
|
|
|
return request;
|
|
}
|
|
|
|
//some status codes represent recoverable error codes
|
|
//we try sending again some point later
|
|
- (BOOL)shouldDeleteDataWithStatusCode:(NSInteger)statusCode {
|
|
NSArray *recoverableStatusCodes = @[@429, @408, @500, @503, @511];
|
|
|
|
return ![recoverableStatusCodes containsObject:@(statusCode)];
|
|
}
|
|
|
|
#pragma mark - Getter/Setter
|
|
|
|
- (NSOperationQueue *)operationQueue {
|
|
if(nil == _operationQueue) {
|
|
_operationQueue = [[NSOperationQueue alloc] init];
|
|
_operationQueue.maxConcurrentOperationCount = defaultRequestLimit;
|
|
}
|
|
return _operationQueue;
|
|
}
|
|
|
|
- (NSUInteger)runningRequestsCount {
|
|
@synchronized(self) {
|
|
return _runningRequestsCount;
|
|
}
|
|
}
|
|
|
|
- (void)setRunningRequestsCount:(NSUInteger)runningRequestsCount {
|
|
@synchronized(self) {
|
|
_runningRequestsCount = runningRequestsCount;
|
|
}
|
|
}
|
|
|
|
@end
|