mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Fix MtProto memory leaks
This commit is contained in:
parent
0cd1770a0a
commit
1cd77c768b
@ -1,110 +0,0 @@
|
||||
|
||||
|
||||
// AFHTTPOperation.h
|
||||
//
|
||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MtProtoKit/AFURLConnectionOperation.h>
|
||||
|
||||
|
||||
/**
|
||||
`AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request.
|
||||
*/
|
||||
@interface AFHTTPRequestOperation : AFURLConnectionOperation
|
||||
|
||||
///----------------------------------------------
|
||||
/// @name Getting HTTP URL Connection Information
|
||||
///----------------------------------------------
|
||||
|
||||
/**
|
||||
The last HTTP response received by the operation's connection.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSHTTPURLResponse *response;
|
||||
|
||||
|
||||
///----------------------------------------------------------
|
||||
/// @name Managing And Checking For Acceptable HTTP Responses
|
||||
///----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Returns an `NSIndexSet` object containing the ranges of acceptable HTTP status codes. When non-`nil`, the operation will set the `error` property to an error in `AFErrorDomain`. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
|
||||
By default, this is the range 200 to 299, inclusive.
|
||||
*/
|
||||
@property (nonatomic, strong) NSIndexSet *acceptableStatusCodes;
|
||||
|
||||
/**
|
||||
A Boolean value that corresponds to whether the status code of the response is within the specified set of acceptable status codes. Returns `YES` if `acceptableStatusCodes` is `nil`.
|
||||
*/
|
||||
@property (readonly) BOOL hasAcceptableStatusCode;
|
||||
|
||||
/**
|
||||
Returns an `NSSet` object containing the acceptable MIME types. When non-`nil`, the operation will set the `error` property to an error in `AFErrorDomain`. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
|
||||
|
||||
By default, this is `nil`.
|
||||
*/
|
||||
@property (nonatomic, strong) NSSet *acceptableContentTypes;
|
||||
|
||||
/**
|
||||
A Boolean value that corresponds to whether the MIME type of the response is among the specified set of acceptable content types. Returns `YES` if `acceptableContentTypes` is `nil`.
|
||||
*/
|
||||
@property (readonly) BOOL hasAcceptableContentType;
|
||||
|
||||
/**
|
||||
The callback dispatch queue on success. If `NULL` (default), the main queue is used.
|
||||
*/
|
||||
@property (nonatomic, strong) dispatch_queue_t successCallbackQueue;
|
||||
|
||||
/**
|
||||
The callback dispatch queue on failure. If `NULL` (default), the main queue is used.
|
||||
*/
|
||||
@property (nonatomic, strong) dispatch_queue_t failureCallbackQueue;
|
||||
|
||||
/**
|
||||
The dispatch group on which to call the completion/failure block
|
||||
*/
|
||||
@property (nonatomic, strong) dispatch_group_t dispatchGroup;
|
||||
|
||||
/**
|
||||
A Boolean value determining whether or not the class can process the specified request. For example, `AFJSONRequestOperation` may check to make sure the content type was `application/json` or the URL path extension was `.json`.
|
||||
|
||||
@param urlRequest The request that is determined to be supported or not supported for this class.
|
||||
*/
|
||||
+ (BOOL)canProcessRequest:(NSURLRequest *)urlRequest;
|
||||
|
||||
///-----------------------------------------------------------
|
||||
/// @name Setting Completion Block Success / Failure Callbacks
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed.
|
||||
|
||||
@param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request.
|
||||
@param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occured during the request.
|
||||
|
||||
@discussion This method should be overridden in subclasses in order to specify the response object passed into the success block.
|
||||
*/
|
||||
- (void)setCompletionBlockWithSuccess:(void (^)(NSOperation *operation, id responseObject))success
|
||||
failure:(void (^)(NSOperation *operation, NSError *error))failure;
|
||||
|
||||
@end
|
@ -1,208 +0,0 @@
|
||||
|
||||
|
||||
// AFURLConnectionOperation.h
|
||||
//
|
||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
Indicates an error occured in AFNetworking.
|
||||
|
||||
@discussion Error codes for AFNetworkingErrorDomain correspond to codes in NSURLErrorDomain.
|
||||
*/
|
||||
extern NSString * const AFNetworkingErrorDomain;
|
||||
|
||||
/**
|
||||
Posted when an operation begins executing.
|
||||
*/
|
||||
extern NSString * const AFNetworkingOperationDidStartNotification;
|
||||
|
||||
/**
|
||||
Posted when an operation finishes.
|
||||
*/
|
||||
extern NSString * const AFNetworkingOperationDidFinishNotification;
|
||||
|
||||
/**
|
||||
`AFURLConnectionOperation` is an `NSOperation` that implements NSURLConnection delegate methods.
|
||||
|
||||
## Subclassing Notes
|
||||
|
||||
This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors.
|
||||
|
||||
If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes.
|
||||
|
||||
## NSURLConnection Delegate Methods
|
||||
|
||||
`AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods:
|
||||
|
||||
- `connection:didReceiveResponse:`
|
||||
- `connection:didReceiveData:`
|
||||
- `connectionDidFinishLoading:`
|
||||
- `connection:didFailWithError:`
|
||||
- `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:`
|
||||
- `connection:willCacheResponse:`
|
||||
- `connection:canAuthenticateAgainstProtectionSpace:`
|
||||
- `connection:didReceiveAuthenticationChallenge:`
|
||||
|
||||
If any of these methods are overriden in a subclass, they _must_ call the `super` implementation first.
|
||||
|
||||
## Class Constructors
|
||||
|
||||
Class constructors, or methods that return an unowned (zero retain count) instance, are the preferred way for subclasses to encapsulate any particular logic for handling the setup or parsing of response data. For instance, `AFJSONRequestOperation` provides `JSONRequestOperationWithRequest:success:failure:`, which takes block arguments, whose parameter on for a successful request is the JSON object initialized from the `response data`.
|
||||
|
||||
## Callbacks and Completion Blocks
|
||||
|
||||
The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (e.g. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this.
|
||||
|
||||
@warning Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as "The Deallocation Problem" (See http://developer.apple.com/library/ios/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11)
|
||||
|
||||
@warning Attempting to load a `file://` URL in iOS 4 may result in an `NSInvalidArgumentException`, caused by the connection returning `NSURLResponse` rather than `NSHTTPURLResponse`, which is the behavior as of iOS 5.
|
||||
*/
|
||||
@interface AFURLConnectionOperation : NSOperation
|
||||
|
||||
///-------------------------------
|
||||
/// @name Accessing Run Loop Modes
|
||||
///-------------------------------
|
||||
|
||||
/**
|
||||
The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`.
|
||||
*/
|
||||
@property (nonatomic, strong) NSSet *runLoopModes;
|
||||
|
||||
///-----------------------------------------
|
||||
/// @name Getting URL Connection Information
|
||||
///-----------------------------------------
|
||||
|
||||
/**
|
||||
The request used by the operation's connection.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSURLRequest *request;
|
||||
|
||||
/**
|
||||
The last response received by the operation's connection.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSURLResponse *response;
|
||||
|
||||
/**
|
||||
The error, if any, that occured in the lifecycle of the request.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSError *error;
|
||||
|
||||
///----------------------------
|
||||
/// @name Getting Response Data
|
||||
///----------------------------
|
||||
|
||||
/**
|
||||
The data received during the request.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSData *responseData;
|
||||
|
||||
/**
|
||||
The string representation of the response data.
|
||||
|
||||
@discussion This method uses the string encoding of the response, or if UTF-8 if not specified, to construct a string from the response data.
|
||||
*/
|
||||
@property (readonly, nonatomic, copy) NSString *responseString;
|
||||
|
||||
///------------------------
|
||||
/// @name Accessing Streams
|
||||
///------------------------
|
||||
|
||||
/**
|
||||
The input stream used to read data to be sent during the request.
|
||||
|
||||
@discussion This property acts as a proxy to the `HTTPBodyStream` property of `request`.
|
||||
*/
|
||||
@property (nonatomic, strong) NSInputStream *inputStream;
|
||||
|
||||
/**
|
||||
The output stream that is used to write data received until the request is finished.
|
||||
|
||||
@discussion By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`.
|
||||
*/
|
||||
@property (nonatomic, strong) NSOutputStream *outputStream;
|
||||
|
||||
///------------------------------------------------------
|
||||
/// @name Initializing an AFURLConnectionOperation Object
|
||||
///------------------------------------------------------
|
||||
|
||||
/**
|
||||
Initializes and returns a newly allocated operation object with a url connection configured with the specified url request.
|
||||
|
||||
@param urlRequest The request object to be used by the operation connection.
|
||||
|
||||
@discussion This is the designated initializer.
|
||||
*/
|
||||
- (id)initWithRequest:(NSURLRequest *)urlRequest;
|
||||
|
||||
///---------------------------------
|
||||
/// @name Setting Progress Callbacks
|
||||
///---------------------------------
|
||||
|
||||
/**
|
||||
Sets a callback to be called when an undetermined number of bytes have been uploaded to the server.
|
||||
|
||||
@param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times.
|
||||
|
||||
@see setDownloadProgressBlock
|
||||
*/
|
||||
- (void)setUploadProgressBlock:(void (^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block;
|
||||
|
||||
/**
|
||||
Sets a callback to be called when an undetermined number of bytes have been downloaded from the server.
|
||||
|
||||
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times.
|
||||
|
||||
@see setUploadProgressBlock
|
||||
*/
|
||||
- (void)setDownloadProgressBlock:(void (^)(NSInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block;
|
||||
|
||||
///-------------------------------------------------
|
||||
/// @name Setting NSURLConnection Delegate Callbacks
|
||||
///-------------------------------------------------
|
||||
|
||||
/**
|
||||
Sets a block to be executed to determine whether the connection should be able to respond to a protection space's form of authentication, as handled by the `NSURLConnectionDelegate` method `connection:canAuthenticateAgainstProtectionSpace:`.
|
||||
|
||||
@param block A block object to be executed to determine whether the connection should be able to respond to a protection space's form of authentication. The block has a `BOOL` return type and takes two arguments: the URL connection object, and the protection space to authenticate against.
|
||||
|
||||
@discussion If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined, `connection:canAuthenticateAgainstProtectionSpace:` will accept invalid SSL certificates, returning `YES` if the protection space authentication method is `NSURLAuthenticationMethodServerTrust`.
|
||||
*/
|
||||
- (void)setAuthenticationAgainstProtectionSpaceBlock:(BOOL (^)(NSURLConnection *connection, NSURLProtectionSpace *protectionSpace))block;
|
||||
|
||||
/**
|
||||
Sets a block to be executed when the connection must authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:didReceiveAuthenticationChallenge:`.
|
||||
|
||||
@param block A block object to be executed when the connection must authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated.
|
||||
|
||||
@discussion If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined, `connection:didReceiveAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates.
|
||||
*/
|
||||
- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
|
||||
|
||||
/**
|
||||
Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`.
|
||||
|
||||
@param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request.
|
||||
*/
|
||||
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
|
||||
|
||||
@end
|
@ -1,8 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MtProtoKit/MTDatacenterAuthInfo.h>
|
||||
#import <MtProtoKit/AFHTTPRequestOperation.h>
|
||||
#import <MtProtoKit/AFURLConnectionOperation.h>
|
||||
#import <MtProtoKit/MTApiEnvironment.h>
|
||||
#import <MtProtoKit/MTAtomic.h>
|
||||
#import <MtProtoKit/MTBackupAddressSignals.h>
|
||||
|
@ -1,212 +0,0 @@
|
||||
// AFHTTPOperation.m
|
||||
//
|
||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <MtProtoKit/AFHTTPRequestOperation.h>
|
||||
|
||||
static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
|
||||
NSMutableString *string = [NSMutableString string];
|
||||
|
||||
NSRange range = NSMakeRange([indexSet firstIndex], 1);
|
||||
while (range.location != NSNotFound) {
|
||||
NSUInteger nextIndex = [indexSet indexGreaterThanIndex:range.location];
|
||||
while (nextIndex == range.location + range.length) {
|
||||
range.length++;
|
||||
nextIndex = [indexSet indexGreaterThanIndex:nextIndex];
|
||||
}
|
||||
|
||||
if (string.length) {
|
||||
[string appendString:@","];
|
||||
}
|
||||
|
||||
if (range.length == 1) {
|
||||
[string appendFormat:@"%lu", (unsigned long)range.location];
|
||||
} else {
|
||||
NSUInteger firstIndex = range.location;
|
||||
NSUInteger lastIndex = firstIndex + range.length - 1;
|
||||
[string appendFormat:@"%lu-%lu", (unsigned long)firstIndex, (unsigned long)lastIndex];
|
||||
}
|
||||
|
||||
range.location = nextIndex;
|
||||
range.length = 1;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFHTTPRequestOperation ()
|
||||
@property (readwrite, nonatomic, strong) NSError *HTTPError;
|
||||
@property (nonatomic) dispatch_once_t onceToken;
|
||||
@property (atomic) dispatch_semaphore_t dispatchSemaphore;
|
||||
@end
|
||||
|
||||
@implementation AFHTTPRequestOperation
|
||||
@synthesize acceptableStatusCodes = _acceptableStatusCodes;
|
||||
@synthesize acceptableContentTypes = _acceptableContentTypes;
|
||||
@synthesize HTTPError = _HTTPError;
|
||||
@synthesize successCallbackQueue = _successCallbackQueue;
|
||||
@synthesize failureCallbackQueue = _failureCallbackQueue;
|
||||
@synthesize dispatchGroup = _dispatchGroup;
|
||||
@synthesize onceToken = _onceToken;
|
||||
@synthesize dispatchSemaphore = _dispatchSemaphore;
|
||||
|
||||
|
||||
- (id)initWithRequest:(NSURLRequest *)request {
|
||||
self = [super initWithRequest:request];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
|
||||
self.dispatchSemaphore = dispatch_semaphore_create(1);
|
||||
self.completionBlock = NULL;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSHTTPURLResponse *)response {
|
||||
return (NSHTTPURLResponse *)[super response];
|
||||
}
|
||||
|
||||
- (NSError *)error {
|
||||
if (self.response && !self.HTTPError) {
|
||||
if (![self hasAcceptableStatusCode]) {
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code in (%@), got %ld", nil), AFStringFromIndexSet(self.acceptableStatusCodes), (long)[self.response statusCode]] forKey:NSLocalizedDescriptionKey];
|
||||
[userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
|
||||
|
||||
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
|
||||
} else if ([self.responseData length] > 0 && ![self hasAcceptableContentType]) { // Don't invalidate content type if there is no content
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected content type %@, got %@", nil), self.acceptableContentTypes, [self.response MIMEType]] forKey:NSLocalizedDescriptionKey];
|
||||
[userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
|
||||
|
||||
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
if (self.HTTPError) {
|
||||
return self.HTTPError;
|
||||
} else {
|
||||
return [super error];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasAcceptableStatusCode {
|
||||
return !self.acceptableStatusCodes || [self.acceptableStatusCodes containsIndex:(NSUInteger)[self.response statusCode]];
|
||||
}
|
||||
|
||||
- (BOOL)hasAcceptableContentType {
|
||||
return !self.acceptableContentTypes || [self.acceptableContentTypes containsObject:[self.response MIMEType]];
|
||||
}
|
||||
|
||||
- (void)setSuccessCallbackQueue:(dispatch_queue_t)successCallbackQueue {
|
||||
if (successCallbackQueue != _successCallbackQueue) {
|
||||
_successCallbackQueue = successCallbackQueue;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFailureCallbackQueue:(dispatch_queue_t)failureCallbackQueue {
|
||||
if (failureCallbackQueue != _failureCallbackQueue) {
|
||||
_failureCallbackQueue = failureCallbackQueue;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDispatchGroup:(dispatch_group_t)dispatchGroup {
|
||||
dispatch_semaphore_wait(self.dispatchSemaphore, DISPATCH_TIME_FOREVER);
|
||||
if (dispatchGroup != _dispatchGroup) {
|
||||
if (_dispatchGroup) {
|
||||
dispatch_group_leave(_dispatchGroup);
|
||||
_dispatchGroup = NULL;
|
||||
}
|
||||
|
||||
if (dispatchGroup) {
|
||||
_dispatchGroup = dispatchGroup;
|
||||
dispatch_group_enter(_dispatchGroup);
|
||||
}
|
||||
}
|
||||
dispatch_semaphore_signal(self.dispatchSemaphore);
|
||||
}
|
||||
|
||||
- (dispatch_group_t)dispatchGroup {
|
||||
dispatch_semaphore_wait(self.dispatchSemaphore, DISPATCH_TIME_FOREVER);
|
||||
if(_dispatchGroup == NULL) {
|
||||
_dispatchGroup = dispatch_group_create();
|
||||
dispatch_group_enter(_dispatchGroup);
|
||||
}
|
||||
dispatch_semaphore_signal(self.dispatchSemaphore);
|
||||
return _dispatchGroup;
|
||||
}
|
||||
|
||||
- (void)setCompletionBlock:(void (^)(void))block {
|
||||
__block id _blockSelf = self;
|
||||
dispatch_once_t *blockOnceToken = &_onceToken;
|
||||
|
||||
[super setCompletionBlock:^{
|
||||
if(block) {
|
||||
block();
|
||||
}
|
||||
// Dispatch once is used to ensure that setting the block with this block will not cause multiple calls to 'dispatch_group_leave'
|
||||
dispatch_once(blockOnceToken, ^{
|
||||
dispatch_group_leave([_blockSelf dispatchGroup]);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setCompletionBlockWithSuccess:(void (^)(NSOperation *operation, id responseObject))success
|
||||
failure:(void (^)(NSOperation *operation, NSError *error))failure
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.completionBlock = ^ {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([strongSelf isCancelled]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strongSelf.error) {
|
||||
if (failure) {
|
||||
dispatch_group_async(strongSelf.dispatchGroup, strongSelf.failureCallbackQueue ? strongSelf.failureCallbackQueue : dispatch_get_main_queue(), ^{
|
||||
failure(strongSelf, strongSelf.error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
dispatch_group_async(strongSelf.dispatchGroup, strongSelf.successCallbackQueue ? strongSelf.successCallbackQueue : dispatch_get_main_queue(), ^{
|
||||
success(strongSelf, strongSelf.responseData);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#pragma mark - AFHTTPClientOperation
|
||||
|
||||
+ (BOOL)canProcessRequest:(NSURLRequest *)__unused request {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
@ -1,528 +0,0 @@
|
||||
// AFURLConnectionOperation.m
|
||||
//
|
||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <MtProtoKit/AFURLConnectionOperation.h>
|
||||
|
||||
//#import "AFImageRequestOperation.h"
|
||||
|
||||
//#define AF_DEBUGLOCK
|
||||
|
||||
typedef enum {
|
||||
AFHTTPOperationReadyState = 1,
|
||||
AFHTTPOperationExecutingState = 2,
|
||||
AFHTTPOperationFinishedState = 3,
|
||||
} _AFOperationState;
|
||||
|
||||
typedef unsigned short AFOperationState;
|
||||
|
||||
static NSUInteger const kAFHTTPMinimumInitialDataCapacity = 1024;
|
||||
static NSUInteger const kAFHTTPMaximumInitialDataCapacity = 1024 * 1024 * 8;
|
||||
|
||||
static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock";
|
||||
|
||||
NSString * const AFNetworkingErrorDomain = @"com.alamofire.networking.error";
|
||||
|
||||
NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
|
||||
NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";
|
||||
|
||||
typedef void (^AFURLConnectionOperationProgressBlock)(NSInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected);
|
||||
typedef BOOL (^AFURLConnectionOperationAuthenticationAgainstProtectionSpaceBlock)(NSURLConnection *connection, NSURLProtectionSpace *protectionSpace);
|
||||
typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
|
||||
typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
|
||||
|
||||
static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
|
||||
switch (state) {
|
||||
case AFHTTPOperationReadyState:
|
||||
return @"isReady";
|
||||
case AFHTTPOperationExecutingState:
|
||||
return @"isExecuting";
|
||||
case AFHTTPOperationFinishedState:
|
||||
return @"isFinished";
|
||||
default:
|
||||
return @"state";
|
||||
}
|
||||
}
|
||||
|
||||
static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {
|
||||
switch (fromState) {
|
||||
case AFHTTPOperationReadyState:
|
||||
switch (toState) {
|
||||
case AFHTTPOperationExecutingState:
|
||||
return YES;
|
||||
case AFHTTPOperationFinishedState:
|
||||
return isCancelled;
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
case AFHTTPOperationExecutingState:
|
||||
switch (toState) {
|
||||
case AFHTTPOperationFinishedState:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
case AFHTTPOperationFinishedState:
|
||||
return NO;
|
||||
default:
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
@interface AFURLConnectionOperation ()
|
||||
@property (readwrite, nonatomic, assign) AFOperationState state;
|
||||
@property (readwrite, nonatomic, assign, getter = isCancelled) BOOL cancelled;
|
||||
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
|
||||
@property (readwrite, nonatomic, strong) NSURLConnection *connection;
|
||||
@property (readwrite, nonatomic, strong) NSURLRequest *request;
|
||||
@property (readwrite, nonatomic, strong) NSURLResponse *response;
|
||||
@property (readwrite, nonatomic, strong) NSError *error;
|
||||
@property (readwrite, nonatomic, strong) NSData *responseData;
|
||||
@property (readwrite, nonatomic, copy) NSString *responseString;
|
||||
@property (readwrite, nonatomic, assign) NSInteger totalBytesRead;
|
||||
@property (readwrite, nonatomic, strong) NSMutableData *dataAccumulator;
|
||||
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress;
|
||||
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress;
|
||||
@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationAgainstProtectionSpaceBlock authenticationAgainstProtectionSpace;
|
||||
@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge;
|
||||
@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
|
||||
|
||||
- (void)operationDidStart;
|
||||
- (void)finish;
|
||||
@end
|
||||
|
||||
@implementation AFURLConnectionOperation
|
||||
@synthesize state = _state;
|
||||
@synthesize cancelled = _cancelled;
|
||||
@synthesize connection = _connection;
|
||||
@synthesize runLoopModes = _runLoopModes;
|
||||
@synthesize request = _request;
|
||||
@synthesize response = _response;
|
||||
@synthesize error = _error;
|
||||
@synthesize responseData = _responseData;
|
||||
@synthesize responseString = _responseString;
|
||||
@synthesize totalBytesRead = _totalBytesRead;
|
||||
@synthesize dataAccumulator = _dataAccumulator;
|
||||
@dynamic inputStream;
|
||||
@synthesize outputStream = _outputStream;
|
||||
@synthesize uploadProgress = _uploadProgress;
|
||||
@synthesize downloadProgress = _downloadProgress;
|
||||
@synthesize authenticationAgainstProtectionSpace = _authenticationAgainstProtectionSpace;
|
||||
@synthesize authenticationChallenge = _authenticationChallenge;
|
||||
@synthesize cacheResponse = _cacheResponse;
|
||||
@synthesize lock = _lock;
|
||||
|
||||
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
|
||||
do {
|
||||
@autoreleasepool {
|
||||
[NSThread currentThread].threadPriority = 0.2;
|
||||
NSException *caughtException = nil;
|
||||
@try {
|
||||
@autoreleasepool {
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
}
|
||||
@catch(NSException *e) { caughtException = e; }
|
||||
if(caughtException) {
|
||||
NSLog(NSLocalizedString(@"Unhandled exception on %@ networking thread: %@, userInfo: %@", nil), NSStringFromClass([self class]), caughtException, [caughtException userInfo]);
|
||||
}
|
||||
}
|
||||
} while (YES);
|
||||
}
|
||||
|
||||
+ (NSThread *)networkRequestThread {
|
||||
static NSThread *_networkRequestThread = nil;
|
||||
static dispatch_once_t oncePredicate;
|
||||
|
||||
dispatch_once(&oncePredicate, ^{
|
||||
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
|
||||
[_networkRequestThread start];
|
||||
});
|
||||
|
||||
return _networkRequestThread;
|
||||
}
|
||||
|
||||
- (id)initWithRequest:(NSURLRequest *)urlRequest {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.lock = [[NSRecursiveLock alloc] init];
|
||||
self.lock.name = kAFNetworkingLockName;
|
||||
|
||||
self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
|
||||
|
||||
self.request = urlRequest;
|
||||
|
||||
self.state = AFHTTPOperationReadyState;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: dealloc thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
|
||||
if (_outputStream) {
|
||||
[_outputStream close];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response];
|
||||
}
|
||||
|
||||
- (void)setCompletionBlock:(void (^)(void))block {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: setCompletionBlock thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
[self.lock lock];
|
||||
if (!block) {
|
||||
[super setCompletionBlock:nil];
|
||||
} else {
|
||||
__block id _blockSelf = self;
|
||||
[super setCompletionBlock:^ {
|
||||
//TGLog(@"===== Completed %@", [_blockSelf request].URL);
|
||||
block();
|
||||
[_blockSelf setCompletionBlock:nil];
|
||||
}];
|
||||
}
|
||||
[self.lock unlock];
|
||||
}
|
||||
|
||||
- (NSInputStream *)inputStream {
|
||||
return self.request.HTTPBodyStream;
|
||||
}
|
||||
|
||||
- (void)setInputStream:(NSInputStream *)inputStream {
|
||||
NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
|
||||
mutableRequest.HTTPBodyStream = inputStream;
|
||||
self.request = mutableRequest;
|
||||
}
|
||||
|
||||
- (void)setUploadProgressBlock:(void (^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block {
|
||||
self.uploadProgress = block;
|
||||
}
|
||||
|
||||
- (void)setDownloadProgressBlock:(void (^)(NSInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block {
|
||||
self.downloadProgress = block;
|
||||
}
|
||||
|
||||
- (void)setAuthenticationAgainstProtectionSpaceBlock:(BOOL (^)(NSURLConnection *, NSURLProtectionSpace *))block {
|
||||
self.authenticationAgainstProtectionSpace = block;
|
||||
}
|
||||
|
||||
- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block {
|
||||
self.authenticationChallenge = block;
|
||||
}
|
||||
|
||||
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block {
|
||||
self.cacheResponse = block;
|
||||
}
|
||||
|
||||
- (void)setState:(AFOperationState)state {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: setState thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
[self.lock lock];
|
||||
if (AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
|
||||
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
|
||||
NSString *newStateKey = AFKeyPathFromOperationState(state);
|
||||
|
||||
[self willChangeValueForKey:newStateKey];
|
||||
[self willChangeValueForKey:oldStateKey];
|
||||
_state = state;
|
||||
[self didChangeValueForKey:oldStateKey];
|
||||
[self didChangeValueForKey:newStateKey];
|
||||
|
||||
switch (state) {
|
||||
case AFHTTPOperationExecutingState:
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
|
||||
break;
|
||||
case AFHTTPOperationFinishedState:
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
[self.lock unlock];
|
||||
}
|
||||
|
||||
- (NSString *)responseString {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: responseString thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
[self.lock lock];
|
||||
if (!_responseString && self.response && self.responseData) {
|
||||
NSStringEncoding textEncoding = NSUTF8StringEncoding;
|
||||
if (self.response.textEncodingName) {
|
||||
textEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)self.response.textEncodingName));
|
||||
}
|
||||
|
||||
self.responseString = [[NSString alloc] initWithData:self.responseData encoding:textEncoding];
|
||||
}
|
||||
[self.lock unlock];
|
||||
|
||||
return _responseString;
|
||||
}
|
||||
|
||||
#pragma mark - NSOperation
|
||||
|
||||
- (BOOL)isReady {
|
||||
return self.state == AFHTTPOperationReadyState && [super isReady];
|
||||
}
|
||||
|
||||
- (BOOL)isExecuting {
|
||||
return self.state == AFHTTPOperationExecutingState;
|
||||
}
|
||||
|
||||
- (BOOL)isFinished {
|
||||
return self.state == AFHTTPOperationFinishedState;
|
||||
}
|
||||
|
||||
- (BOOL)isConcurrent {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
[self.lock lock];
|
||||
if ([self isReady]) {
|
||||
self.state = AFHTTPOperationExecutingState;
|
||||
|
||||
//TGLog(@"----- Start url %@", self.request.URL);
|
||||
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
|
||||
}
|
||||
[self.lock unlock];
|
||||
}
|
||||
|
||||
- (void)operationDidStart {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: operationDidStart thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
[self.lock lock];
|
||||
if ([self isCancelled]) {
|
||||
[self finish];
|
||||
} else {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
||||
for (NSString *runLoopMode in self.runLoopModes) {
|
||||
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
|
||||
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
|
||||
}
|
||||
|
||||
[self.connection start];
|
||||
}
|
||||
[self.lock unlock];
|
||||
}
|
||||
|
||||
- (void)finish {
|
||||
self.state = AFHTTPOperationFinishedState;
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
#ifdef AF_DEBUGLOCK
|
||||
if ([NSThread currentThread] != [AFURLConnectionOperation networkRequestThread])
|
||||
NSLog(@"0x%x: cancel thread %@", (int)self, [NSThread currentThread]);
|
||||
#endif
|
||||
[self.lock lock];
|
||||
if (![self isFinished] && ![self isCancelled]) {
|
||||
[self willChangeValueForKey:@"isCancelled"];
|
||||
_cancelled = YES;
|
||||
[super cancel];
|
||||
[self didChangeValueForKey:@"isCancelled"];
|
||||
|
||||
// Cancel the connection on the thread it runs on to prevent race conditions
|
||||
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
|
||||
}
|
||||
[self.lock unlock];
|
||||
}
|
||||
|
||||
- (void)cancelConnection {
|
||||
if (self.connection) {
|
||||
[self.connection cancel];
|
||||
|
||||
// Manually send this delegate message since `[self.connection cancel]` causes the connection to never send another message to its delegate
|
||||
NSDictionary *userInfo = nil;
|
||||
if ([self.request URL]) {
|
||||
userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
|
||||
}
|
||||
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSURLConnectionDelegate
|
||||
|
||||
- (BOOL)connection:(NSURLConnection *)connection
|
||||
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
|
||||
{
|
||||
#ifdef _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
|
||||
if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
return YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self.authenticationAgainstProtectionSpace) {
|
||||
return self.authenticationAgainstProtectionSpace(connection, protectionSpace);
|
||||
} else if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
{
|
||||
#ifdef _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self.authenticationChallenge) {
|
||||
self.authenticationChallenge(connection, challenge);
|
||||
} else {
|
||||
if ([challenge previousFailureCount] == 0) {
|
||||
NSURLCredential *credential = nil;
|
||||
|
||||
NSString *username = CFBridgingRelease(CFURLCopyUserName((CFURLRef)[self.request URL]));
|
||||
NSString *password = CFBridgingRelease(CFURLCopyPassword((CFURLRef)[self.request URL]));
|
||||
|
||||
if (username && password) {
|
||||
credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceNone];
|
||||
} else if (username) {
|
||||
credential = [[[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:[challenge protectionSpace]] objectForKey:username];
|
||||
} else {
|
||||
credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:[challenge protectionSpace]];
|
||||
}
|
||||
|
||||
if (credential) {
|
||||
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
|
||||
} else {
|
||||
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
|
||||
}
|
||||
} else {
|
||||
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)__unused connection
|
||||
didSendBodyData:(NSInteger)bytesWritten
|
||||
totalBytesWritten:(NSInteger)totalBytesWritten
|
||||
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
|
||||
{
|
||||
if (self.uploadProgress) {
|
||||
self.uploadProgress(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)__unused connection
|
||||
didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
self.response = (NSHTTPURLResponse *)response;
|
||||
|
||||
if (self.outputStream) {
|
||||
[self.outputStream open];
|
||||
} else {
|
||||
NSUInteger maxCapacity = MAX((NSUInteger)llabs(response.expectedContentLength), kAFHTTPMinimumInitialDataCapacity);
|
||||
NSUInteger capacity = MIN(maxCapacity, kAFHTTPMaximumInitialDataCapacity);
|
||||
self.dataAccumulator = [NSMutableData dataWithCapacity:capacity];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)__unused connection
|
||||
didReceiveData:(NSData *)data
|
||||
{
|
||||
self.totalBytesRead += [data length];
|
||||
|
||||
if (self.outputStream) {
|
||||
if ([self.outputStream hasSpaceAvailable]) {
|
||||
const uint8_t *dataBuffer = (uint8_t *) [data bytes];
|
||||
[self.outputStream write:&dataBuffer[0] maxLength:[data length]];
|
||||
}
|
||||
} else {
|
||||
[self.dataAccumulator appendData:data];
|
||||
}
|
||||
|
||||
if (self.downloadProgress) {
|
||||
self.downloadProgress([data length], self.totalBytesRead, (NSInteger)self.response.expectedContentLength);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)__unused connection {
|
||||
if (self.outputStream) {
|
||||
[self.outputStream close];
|
||||
} else {
|
||||
self.responseData = [NSData dataWithData:self.dataAccumulator];
|
||||
_dataAccumulator = nil;
|
||||
}
|
||||
|
||||
[self finish];
|
||||
|
||||
self.connection = nil;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)__unused connection
|
||||
didFailWithError:(NSError *)error
|
||||
{
|
||||
self.error = error;
|
||||
|
||||
if (self.outputStream) {
|
||||
[self.outputStream close];
|
||||
} else {
|
||||
_dataAccumulator = nil;
|
||||
}
|
||||
|
||||
[self finish];
|
||||
|
||||
self.connection = nil;
|
||||
}
|
||||
|
||||
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
|
||||
willCacheResponse:(NSCachedURLResponse *)cachedResponse
|
||||
{
|
||||
if (self.cacheResponse) {
|
||||
return self.cacheResponse(connection, cachedResponse);
|
||||
} else {
|
||||
if ([self isCancelled]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return cachedResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -1261,16 +1261,25 @@ static void copyKeychainDictionaryKey(NSString * _Nonnull group, NSString * _Non
|
||||
}
|
||||
}] take:1];
|
||||
|
||||
_transportSchemeDisposableByDatacenterId[@(datacenterId)] = [[filteredSignal onDispose:^
|
||||
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
|
||||
_transportSchemeDisposableByDatacenterId[@(datacenterId)] = disposable;
|
||||
|
||||
__weak MTMetaDisposable *weakDisposable = disposable;
|
||||
[disposable setDisposable:[[filteredSignal onDispose:^
|
||||
{
|
||||
__strong MTContext *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
id<MTDisposable> disposable = strongSelf->_transportSchemeDisposableByDatacenterId[@(datacenterId)];
|
||||
[strongSelf->_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
|
||||
[disposable dispose];
|
||||
__strong MTMetaDisposable *strongDisposable = weakDisposable;
|
||||
if (strongDisposable) {
|
||||
id<MTDisposable> disposable = strongSelf->_transportSchemeDisposableByDatacenterId[@(datacenterId)];
|
||||
if (disposable == strongDisposable) {
|
||||
[strongSelf->_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
|
||||
[disposable dispose];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}] startWithNextStrict:^(MTTransportScheme *next)
|
||||
@ -1289,7 +1298,7 @@ static void copyKeychainDictionaryKey(NSString * _Nonnull group, NSString * _Non
|
||||
} completed:^
|
||||
{
|
||||
|
||||
} file:__FILE_NAME__ line:__LINE__];
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#import <MtProtoKit/MTHttpRequestOperation.h>
|
||||
|
||||
#import <MtProtoKit/AFHTTPRequestOperation.h>
|
||||
#import <MtProtoKit/MTDisposable.h>
|
||||
#import <MtProtoKit/MTSignal.h>
|
||||
|
||||
@ -29,30 +28,30 @@
|
||||
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
|
||||
[request setValue:value forHTTPHeaderField:key];
|
||||
}];
|
||||
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
|
||||
|
||||
[operation setSuccessCallbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
|
||||
[operation setFailureCallbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
|
||||
|
||||
[operation setCompletionBlockWithSuccess:^(__unused NSOperation *operation, __unused id responseObject)
|
||||
{
|
||||
AFHTTPRequestOperation *concreteOperation = (AFHTTPRequestOperation *)operation;
|
||||
MTHttpResponse *result = [[MTHttpResponse alloc] initWithHeaders:[concreteOperation response].allHeaderFields data:[concreteOperation responseData]];
|
||||
[subscriber putNext:result];
|
||||
[subscriber putCompletion];
|
||||
} failure:^(__unused NSOperation *operation, __unused NSError *error)
|
||||
{
|
||||
[subscriber putError:error];
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse * response, NSError *error) {
|
||||
if (error) {
|
||||
[subscriber putError:error];
|
||||
} else {
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
MTHttpResponse *result = [[MTHttpResponse alloc] initWithHeaders:httpResponse.allHeaderFields data:data];
|
||||
[subscriber putNext:result];
|
||||
[subscriber putCompletion];
|
||||
} else {
|
||||
[subscriber putError:nil];
|
||||
}
|
||||
}
|
||||
}];
|
||||
[dataTask resume];
|
||||
|
||||
[operation start];
|
||||
|
||||
__weak AFHTTPRequestOperation *weakOperation = operation;
|
||||
|
||||
__weak NSURLSessionDataTask *weakDataTask = dataTask;
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^
|
||||
{
|
||||
__strong AFHTTPRequestOperation *strongOperation = weakOperation;
|
||||
[strongOperation cancel];
|
||||
__strong NSURLSessionDataTask *strongDataTask = weakDataTask;
|
||||
if (strongDataTask) {
|
||||
[strongDataTask cancel];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
#import <MtProtoKit/MTAtomic.h>
|
||||
#import <MtProtoKit/MTBag.h>
|
||||
|
||||
#import <os/lock.h>
|
||||
|
||||
@interface MTSubscriberDisposable : NSObject <MTDisposable>
|
||||
{
|
||||
__weak MTSubscriber *_subscriber;
|
||||
@ -124,7 +126,7 @@
|
||||
|
||||
@interface MTSignalQueueState : NSObject <MTDisposable>
|
||||
{
|
||||
pthread_mutex_t _lock;
|
||||
os_unfair_lock _lock;
|
||||
bool _executingSignal;
|
||||
bool _terminated;
|
||||
|
||||
@ -145,8 +147,6 @@
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
pthread_mutex_init(&_lock, nil);
|
||||
|
||||
_subscriber = subscriber;
|
||||
_currentDisposable = [[MTMetaDisposable alloc] init];
|
||||
_queuedSignals = queueMode ? [[NSMutableArray alloc] init] : nil;
|
||||
@ -155,10 +155,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
- (void)beginWithDisposable:(id<MTDisposable>)disposable
|
||||
{
|
||||
_disposable = disposable;
|
||||
@ -167,9 +163,8 @@
|
||||
- (void)enqueueSignal:(MTSignal *)signal
|
||||
{
|
||||
bool startSignal = false;
|
||||
pthread_mutex_lock(&_lock);
|
||||
if (_queueMode && _executingSignal)
|
||||
{
|
||||
os_unfair_lock_lock(&_lock);
|
||||
if (_queueMode && _executingSignal) {
|
||||
[_queuedSignals addObject:signal];
|
||||
}
|
||||
else
|
||||
@ -177,17 +172,29 @@
|
||||
_executingSignal = true;
|
||||
startSignal = true;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
os_unfair_lock_unlock(&_lock);
|
||||
|
||||
if (startSignal)
|
||||
{
|
||||
__weak MTSignalQueueState *weakSelf = self;
|
||||
id<MTDisposable> disposable = [signal startWithNext:^(id next)
|
||||
{
|
||||
[_subscriber putNext:next];
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
#if DEBUG
|
||||
assert(strongSelf->_subscriber != nil);
|
||||
#endif
|
||||
[strongSelf->_subscriber putNext:next];
|
||||
}
|
||||
} error:^(id error)
|
||||
{
|
||||
[_subscriber putError:error];
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
#if DEBUG
|
||||
assert(strongSelf->_subscriber != nil);
|
||||
#endif
|
||||
[strongSelf->_subscriber putError:error];
|
||||
}
|
||||
} completed:^
|
||||
{
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
@ -205,7 +212,7 @@
|
||||
MTSignal *nextSignal = nil;
|
||||
|
||||
bool terminated = false;
|
||||
pthread_mutex_lock(&_lock);
|
||||
os_unfair_lock_lock(&_lock);
|
||||
_executingSignal = false;
|
||||
|
||||
if (_queueMode)
|
||||
@ -221,7 +228,7 @@
|
||||
}
|
||||
else
|
||||
terminated = _terminated;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
os_unfair_lock_unlock(&_lock);
|
||||
|
||||
if (terminated)
|
||||
[_subscriber putCompletion];
|
||||
@ -230,17 +237,29 @@
|
||||
__weak MTSignalQueueState *weakSelf = self;
|
||||
id<MTDisposable> disposable = [nextSignal startWithNext:^(id next)
|
||||
{
|
||||
[_subscriber putNext:next];
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
#if DEBUG
|
||||
assert(strongSelf->_subscriber != nil);
|
||||
#endif
|
||||
[strongSelf->_subscriber putNext:next];
|
||||
}
|
||||
} error:^(id error)
|
||||
{
|
||||
[_subscriber putError:error];
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
#if DEBUG
|
||||
assert(strongSelf->_subscriber != nil);
|
||||
#endif
|
||||
[strongSelf->_subscriber putError:error];
|
||||
}
|
||||
} completed:^
|
||||
{
|
||||
__strong MTSignalQueueState *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf headCompleted];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
[_currentDisposable setDisposable:disposable];
|
||||
}
|
||||
@ -249,13 +268,14 @@
|
||||
- (void)beginCompletion
|
||||
{
|
||||
bool executingSignal = false;
|
||||
pthread_mutex_lock(&_lock);
|
||||
os_unfair_lock_lock(&_lock);
|
||||
executingSignal = _executingSignal;
|
||||
_terminated = true;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
os_unfair_lock_unlock(&_lock);
|
||||
|
||||
if (!executingSignal)
|
||||
if (!executingSignal) {
|
||||
[_subscriber putCompletion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dispose
|
||||
@ -426,7 +446,7 @@
|
||||
MTMetaDisposable *startDisposable = [[MTMetaDisposable alloc] init];
|
||||
MTMetaDisposable *timerDisposable = [[MTMetaDisposable alloc] init];
|
||||
|
||||
MTTimer *timer = [[MTTimer alloc] initWithTimeout:seconds repeat:false completion:^{
|
||||
MTTimer *timer = [[MTTimer alloc] initWithTimeout:seconds repeat:false completion:^() {
|
||||
[startDisposable setDisposable:[self startWithNext:^(id next)
|
||||
{
|
||||
[subscriber putNext:next];
|
||||
@ -499,15 +519,16 @@
|
||||
{
|
||||
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable> (MTSubscriber *subscriber)
|
||||
{
|
||||
MTDisposableSet *disposable = [[MTDisposableSet alloc] init];
|
||||
MTMetaDisposable *mainDisposable = [[MTMetaDisposable alloc] init];
|
||||
MTMetaDisposable *alternativeDisposable = [[MTMetaDisposable alloc] init];
|
||||
|
||||
[disposable add:[self startWithNext:^(id next)
|
||||
[mainDisposable setDisposable:[self startWithNext:^(id next)
|
||||
{
|
||||
[subscriber putNext:next];
|
||||
} error:^(id error)
|
||||
{
|
||||
MTSignal *signal = f(error);
|
||||
[disposable add:[signal startWithNext:^(id next)
|
||||
[alternativeDisposable setDisposable:[signal startWithNext:^(id next)
|
||||
{
|
||||
[subscriber putNext:next];
|
||||
} error:^(id error)
|
||||
@ -522,7 +543,10 @@
|
||||
[subscriber putCompletion];
|
||||
}]];
|
||||
|
||||
return disposable;
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
[mainDisposable dispose];
|
||||
[alternativeDisposable dispose];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,15 @@ import Foundation
|
||||
public func `catch`<T, E, R>(_ f: @escaping(E) -> Signal<T, R>) -> (Signal<T, E>) -> Signal<T, R> {
|
||||
return { signal in
|
||||
return Signal<T, R> { subscriber in
|
||||
let disposable = DisposableSet()
|
||||
let mainDisposable = MetaDisposable()
|
||||
let alternativeDisposable = MetaDisposable()
|
||||
|
||||
disposable.add(signal.start(next: { next in
|
||||
mainDisposable.set(signal.start(next: { next in
|
||||
subscriber.putNext(next)
|
||||
}, error: { error in
|
||||
let anotherSignal = f(error)
|
||||
|
||||
disposable.add(anotherSignal.start(next: { next in
|
||||
alternativeDisposable.set(anotherSignal.start(next: { next in
|
||||
subscriber.putNext(next)
|
||||
}, error: { error in
|
||||
subscriber.putError(error)
|
||||
@ -21,7 +22,10 @@ public func `catch`<T, E, R>(_ f: @escaping(E) -> Signal<T, R>) -> (Signal<T, E>
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
|
||||
return disposable
|
||||
return ActionDisposable {
|
||||
mainDisposable.dispose()
|
||||
alternativeDisposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,3 +246,25 @@ public func deferred<T, E>(_ generator: @escaping() -> Signal<T, E>) -> Signal<T
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func debug_measureTimeToFirstEvent<T, E>(label: String) -> (Signal<T, E>) -> Signal<T, E> {
|
||||
return { signal in
|
||||
#if DEBUG || true
|
||||
if "".isEmpty {
|
||||
var isFirst = true
|
||||
return Signal { subscriber in
|
||||
let startTimestamp = CFAbsoluteTimeGetCurrent()
|
||||
return signal.start(next: { value in
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
let deltaTime = (CFAbsoluteTimeGetCurrent() - startTimestamp) * 1000.0
|
||||
print("measureTimeToFirstEvent(\(label): \(deltaTime) ms")
|
||||
}
|
||||
subscriber.putNext(value)
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return signal
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user