I am trying to upload photo to my web-server via AFHTTPRequestOperation
. Here is my AFHTTPSessionManager
. All http-requests via this manager
works perfect.
- (AFHTTPSessionManager *)manager
{
if (_manager == nil) {
_manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://my.domain.com/api/v2"]];
[_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:@"username" password:@"password"];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
policy.allowInvalidCertificates = YES;
_manager.securityPolicy = policy;
}
return _manager;
}
But when I am trying to upload photo with progress handling:
- (void)photoSend:(UIImage *)photo
toUsers:(NSArray *)users
completion:(CompletionHandler)completion
progress:(ProgressionHandler)progress
{
NSDictionary *params = @{
@"token":self.token,
@"to_usernames":[users componentsJoinedByString:@","],
};
NSString *urlString = [[NSURL URLWithString:@"photo/send" relativeToURL:self.manager.baseURL] absoluteString];
NSMutableURLRequest *request = [self.manager.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData> formData)
{
[formData appendPartWithFileData:UIImageJPEGRepresentation(photo,0.8)
name:@"photo"
fileName:@"image.jpg"
mimeType:@"image/jpg"];
} error:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
// Following 2 lines does not make sense. Http headers contains auth data without this code
//NSURLCredential *credential = [NSURLCredential credentialWithUser:@"login" password:@"password" persistence:NSURLCredentialPersistenceNone];
//[operation setCredential:credential];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
progress(totalBytesWritten*1.0/totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(operation.response, responseObject, operation.error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(operation.response, nil, error);
}];
[self.manager.operationQueue addOperation:operation];
}
I get following error:
Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x994ff20 {NSErrorFailingURLKey=https://my.domain.com/v2/photo/send, NSErrorFailingURLStringKey=https://my.domain.com/v2/photo/send}
Here is the solution:
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
policy.allowInvalidCertificates = YES;
operation.securityPolicy = policy;
So HTTP basic auth data is provided to operation by manager, but SSL policy - not.
In AFNetworking
you can use the AFSSLPinningMode
to make changes in the security policies so if your server does not have SSL installed use the below code
let operation = AFHTTPRequestOperation(request: YourMutableRequestObject)
let policy = AFSecurityPolicy(pinningMode: AFSSLPinningMode.None)
policy.validatesDomainName = false
policy.allowInvalidCertificates = true
operation.securityPolicy = policy
And if your server is using a certificate you can use the below code
let operation = AFHTTPRequestOperation(request: YourMutableRequestObject)
let policy = AFSecurityPolicy(pinningMode: AFSSLPinningMode.Certificate)
policy.validatesDomainName = false
policy.allowInvalidCertificates = true
operation.securityPolicy = policy
Before using the above code make sure that you have added the cer
certificate in your application bundle, coz if not you will not be able to make the calls, you can get the cer file from your web developer or any authority who is providing you with the cer.
In case if you receive a crt file from the web developer you need to convert it to cer using the OpenSSL
using the below code
openssl x509 -in apache.crt -out yourCertFileName.cer -outform der
That should be enough to make the calls
Update
As per apple's new ATS pattern in iOS 9 you have to provide some permissions inside your info.plist
file.
if you want to make a insecure call give below is the XML that you need to integrate inside the info.plist
(Please note you are not going to get any auto fillers here, you have to view your plist as a source and copy + paste the below)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>yourserver.com</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--Include to specify minimum TLS version-->
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
Apple also provides a way to disable ATS given below is the XML for the same but its not recommended
<key>NSAppTransportSecurity</key>
<dict>
<!--Include to allow all connections -->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
In my case since my server was using a self signed certificate i just changed the AFSSLPinningMode
to Certificate and it worked as expected.
Hope this helps someone
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With