I have been googling this problem for hours now, and I can't seem to find a solution.
The last tutorial I followed was this tutorial explaining how I could send a JSON request over an untrusted SSL connection (in this case, a self-signed certificate).
In short, the code I have tried so far is this one:
This method checks if the token is valid with my web API:
-(BOOL)linked
{
if([self token] == nil)
{
return NO;
}else
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#?token=%#&object=User&action=check_authorized", SPAtajosApiLink, [self token], nil]];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"POST"];
NSLog(#"%#", req);
[req setHTTPBody: [mandatoryParameters dataUsingEncoding: NSUTF8StringEncoding]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
[connection start];
NSDictionary *validity = [NSJSONSerialization
JSONObjectWithData:[NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil]
options:NSJSONReadingMutableContainers
error:nil];
NSLog(#"RESPONSE LINKED %# %#", validity[#"code"], validity[#"message"]);
}
return YES;
}
And I have implemented the NSURLConnectionDelegate methods as follows:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
NSLog(#"API LINK %#", challenge.protectionSpace.host);
if ([challenge.protectionSpace.host isEqualToString:SPAtajosApiLink])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
Yet, my app always crashes with the following message:
2013-10-15 22:48:55.382 Atajos[733:60b] { URL:
https://MY_API_URL/www/?token=657fd6c02b3ba439e69b060f7f7680cc&object=User&action=check_authorized
} 2013-10-15 22:48:58.045 Atajos[733:3b17]
NSURLConnection/CFURLConnection HTTP load failed
(kCFStreamErrorDomainSSL, -9813) 2013-10-15 22:48:58.049
Atajos[733:60b] * Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'data parameter is nil'
* First throw call stack: (0x2ec2de8b 0x38f286c7 0x2ec2ddcd 0x2f5b5d77 0x5df9d 0x62395 0x31433025 0x3141e631 0x313b8be7 0x313b7edd
0x3141dca1 0x3389976d 0x33899357 0x2ebf877f 0x2ebf871b 0x2ebf6ee7
0x2eb61541 0x2eb61323 0x3141cf43 0x314181e5 0x62191 0x39421ab7)
libc++abi.dylib: terminating with uncaught exception of type
NSException
Okay, so NSData, presumably the parameter in NSJSONSerialization is NULL. The question, why?
Bizarrely enough I have a similar code that actually works with a UIWebView interacting with a website over untrusted SSL:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//Need to rebuild base URL. Otherwise I get the parameters which will always fail the check.
NSURL *requestUrl = [request URL];
NSString *basicPathString = [NSString stringWithFormat:#"%#://%#:%#%#", requestUrl.scheme, requestUrl.host, requestUrl.port, requestUrl.path];
NSLog(#"%#", basicPathString);
if([basicPathString isEqualToString:SPAtajosLoginLink])
{
__block NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:request.URL.absoluteURL]
options:NSJSONReadingMutableContainers
error:nil];
NSLog(#"Auth token is %#", jsonResponse[#"token"]);
[self dismissViewControllerAnimated:YES completion:^{
if([jsonResponse[#"code"] isEqualToNumber:[NSNumber numberWithInt:200]])
{
[[PDKeychainBindings sharedKeychainBindings] setObject:jsonResponse[#"token"]
forKey:#"com.shortcutpublicity.ios.atajos.userAccessToken"];
}else
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"No Se Pudo Autenticar", #"Error 500 title")
message:NSLocalizedString(#"Se ha producido un error interno del servidor (500). Por favor, trata de nuevo más tarde.", #"Error describing error 500")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"Aceptar", #"Accept") otherButtonTitles:nil];
[alertView show];
}
}];
}else {
//--------------THIS IS THE RELEVANT PART------------
BOOL result = _Authenticated;
if (!_Authenticated)
{
_FailedRequest = request;
NSURLConnection *trash = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(trash)
{
NSLog(#"Allocated succesfully.");
}
}
return result;
}
return YES;
}
-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURL* baseURL = [NSURL URLWithString:SPatajosAuthLink];
if ([challenge.protectionSpace.host isEqualToString:baseURL.host]) {
NSLog(#"trusting connection to host %#", challenge.protectionSpace.host);
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
} else
NSLog(#"Not trusting connection to host %#", challenge.protectionSpace.host);
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)pResponse {
_Authenticated = YES;
[connection cancel];
[_webView loadRequest:_FailedRequest];
}
I have tried to adapt this code that works with a WebView but I haven't had any luck adapting to use it with a NSURLConnection.
To be honest, I'm not quite sure I know how these workarounds work. I have been stealing and attempting to adapt other people's code to no avail. I have looked at NSURLConnection and NSURLRequest docs and I wasn't able to solve these problems. Please help me out.
We use this category in an #ifedf block for testing... make sure you DO NOT leave this in production:
#ifdef DEBUG
#interface NSURLRequest (IgnoreSSL)
+(BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
#end
#implementation NSURLRequest (IgnoreSSL)
+(BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host {return YES;}
#end
#endif
try this Delegate method
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// SecTrustRef trustRef = [[challenge protectionSpace] serverTrust];
// SecTrustEvaluate(trustRef, NULL);
//
// SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trustRef, 0);
//
// NSData *certificateData = (__bridge NSData *) SecCertificateCopyData(certRef);
// NSLog(#"Subject: %#", [[NSString alloc] initWithData:certificateData encoding:NSUTF8StringEncoding]);
// [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
SecTrustRef trust = challenge.protectionSpace.serverTrust;
NSURLCredential *cred;
cred = [NSURLCredential credentialForTrust:trust];
[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
}
Related
I have an application and I used to get my json data with dataWithContentsOfURL.
But because I need to get it an asynchrone fashion.
Now that I use NSURLConnection to handle this I don't receive any useful data all I get is statuscode 200 in the didReceiveResponse method but didReceiveData is never called.
And at connectionDidFinishDownloading destinationURL returns null.
I have no idea wat couses this problem and I would really appreciate some help.
the delegate
#import "NetWorkToGuiDelegate.h"
#implementation NetWorkToGuiDelegate
#synthesize data;
#synthesize caller;
- (id) init: (SEL) pointer :(NSObject *) c;
{
doWhenDone = pointer;
self.caller = c;
data = [[NSMutableData alloc]init];
return self;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseText = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
NSLog(#"%#",responseText);
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
{
NSLog(#"post download finished");
data = [NSData dataWithContentsOfURL:destinationURL];
NSLog(#"data: %#",data);
NSLog(#"URLconnection %#", connection.currentRequest);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[caller performSelector:doWhenDone withObject:data];
#pragma clang diagnostic pop
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.data setLength:0];
NSHTTPURLResponse *resp= (NSHTTPURLResponsae *) response;
NSLog(#"got responce with status %d",[resp statusCode]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d
{
NSLog(#"data recieved %#",d);
[self.data appendData:d];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", #"")
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil] show];
NSLog(#"failed");
}
// Handle basic authentication challenge if needed
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSString *username = #"username";
NSString *password = #"password";
NSURLCredential *credential = [NSURLCredential credentialWithUser:username
password:password
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
#end
the call
NetWorkToGuiDelegate *nwgd = [[NetWorkToGuiDelegate alloc] init:#selector(login:) :self];
NSString *stringURL = [NSString stringWithFormat:#"%#%#%#%#", dk.baseURL, #"menu?code=",dk.loginCode,#"&v=1"];
NSURL *url = [NSURL URLWithString:stringURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:nwgd];
[urlConnection start];
I'll just add that the call happens on the main thread
Actually there is a lot of points to make the answer of this question and there are so many questions on SO itself
Go through this nice post
And a must Read doc from apple itself.(contains code samples and explanations)
Hope this will solve the problems for you.
EDIT
-(void)startAConnection
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
}
Call startAConnection to start the process
NOTE: Don't forget <NSURLConnectionDelegate> in the .h
I am trying to get data from a secure restful service. I follow many other posts alongwith
How to use NSURLConnection to connect with SSL for an untrusted cert?
But in my case didReceiveAuthenticationChallenge and canAuthenticateAgainstProtectionSpace are not get called.
please help if you could solve the problem or provide me some good example to call secure restful service.
NSURL *url = [NSURL URLWithString:#"https://76.69.53.126:8443/jaxrs/tdgateway/getCountries"];
NSError * error = nil;
NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];
NSLog(#"This is %#",url);
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
NSLog(#"This is canAuthenticateAgainstProtectionSpace");
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSLog(#"This is didReceiveAuthenticationChallenge");
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
It could be different than NSURLAuthenticationMethodServerTrust, so just try this to start:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return YES;
}
You should not cancel it immediately:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSLog(#"This is didReceiveAuthenticationChallenge");
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
NSString *XAPIKEY = #"myapikey";
NSString *LOGINUSER = #"login username";
NSString *LOGINPASS = #"passwords";
// Setup NSURLConnection
- (void) postRequest:(NSString *) requestFun requestData:(NSDictionary *) sendDataDic{
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", #"http://api.domain.com/index.php/api/", requestFun]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request setValue:#"application/x-www-form-urlencoded; charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
[request setValue:XAPIKEY forHTTPHeaderField:#"X-API-KEY"];
if(sendDataDic != nil){
NSString *stringData = [self urlEncodedString:sendDataDic];
[request setHTTPBody:[stringData dataUsingEncoding:NSUTF8StringEncoding]];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[stringData length]] forHTTPHeaderField:#"Content-Length"];
}
[request setHTTPMethod:#"POST"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
- (void) getRequest:(NSString *) requestFun requestData:(NSDictionary *) sendDataDic{
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", #"http://api.domain.com/index.php/api/", requestFun]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
[request setValue:#"application/json; charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
[request setValue:XAPIKEY forHTTPHeaderField:#"X-API-KEY"];
if(sendDataDic != nil){
NSString *stringData = [self urlEncodedString:sendDataDic];
[request setHTTPBody:[stringData dataUsingEncoding:NSUTF8StringEncoding]];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[stringData length]] forHTTPHeaderField:#"Content-Length"];
}
[request setHTTPMethod:#"GET"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
static NSString *toString(id object) {
return [NSString stringWithFormat: #"%#", object];
}
static NSString *urlEncode(id object) {
NSString *string = toString(object);
return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}
-(NSString*) urlEncodedString:(NSDictionary*) ObjDic {
NSMutableArray *parts = [NSMutableArray array];
for (id key in ObjDic) {
id value = [ObjDic objectForKey: key];
NSString *part = [NSString stringWithFormat: #"%#=%#", urlEncode(key), urlEncode(value)];
[parts addObject: part];
}
return [parts componentsJoinedByString: #"&"];
}
// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0) {
NSLog(#"received authentication challenge");
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:LOGINUSER
password:LOGINPASS
persistence:NSURLCredentialPersistenceForSession];
NSLog(#"credential created");
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
NSLog(#"responded to authentication challenge");
} else {
NSLog(#"previous authentication failure");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"response status code: %ld", (long)[httpResponse statusCode]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSString *strData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", strData);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"responded to authentication challenge");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"responded to authentication challenge");
}
Currently I am loading an image from URL in my iPhone app, which is working fine. But now i want to access a protected URL. Please guide me with a piece of code that how can i access URL with credentials(username/password).
The simple code through which my app loaded the image from URL is given Below
NSURL *url = [NSURL URLWithString: #"http://www.somedirectory.com"];
UIImage *image = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];
[Pic setImage:image];
Look For these two callbacks
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
// IF to handle NTLM authentication.
if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodNTLM])
return YES;
// Mostly sent by IIS.
if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
return YES;
return NO;
}
- (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
if ([[[challenge protectionSpace] authenticationMethod] isEqual:NSURLAuthenticationMethodNTLM])
{
[[challenge sender] useCredential:[NSURLCredential
credentialWithUser:Username
password:password
persistence:NSURLCredentialPersistenceNone]
forAuthenticationChallenge:challenge];
}
else if ([[[challenge protectionSpace] authenticationMethod] isEqual:NSURLAuthenticationMethodServerTrust])
{
[[challenge sender] useCredential:[NSURLCredential
credentialWithUser:Username
password:password
persistence:NSURLCredentialPersistenceNone]
forAuthenticationChallenge:challenge];
}
else
{
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
You can use some third party library like ASIHTTPRequest, which makes it very easy.
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:#"username"];
[request setPassword:#"password"];
Or you can use NSURLConnection class, but authentication implementation is a bit tricky.
How to download docx , pdf , image , pptx , or any file from a site which ask for credentials for authentication i tried passing the credentials and still data in the nsdata is something else but it store that detain the file created locally can anybody help in the code for downloading any kind of file.
the code is as follows:
in this buttonClicked is called from other file
DownloadingFile.h
#import "MyAppDelegate.h"
#interface DownloadingFile : NSObject
{
NSMutableData *webData;
NSMutableString *soapResults;
NSURLConnection *conn;
BOOL *elementFound;
BOOL isDoneParsing;
MyAppDelegate *mydelegate;
NSString *userCd,*passWord,*siteUrl;
}
#property (nonatomic ,retain) MyAppDelegate *mydelegate;
-(void)buttonClicked;
-(bool)getIsDone;
#end
DownloadingFile.m
#import "DownloadingFile.h"
#import "iExploreAppDelegate.h"
#implementation DownloadingFile
#synthesize mydelegate;
- (void)buttonClicked
{
mydelegate=(MyAppDelegate *)[[UIApplication sharedApplication] delegate];
userCd=[[NSString alloc] initWithString:[mydelegate getUserId]];
passWord=[[NSString alloc] initWithString:[mydelegate getPassword]];
NSLog(#"In Downloading; ");
NSURL *url =[NSURL URLWithString:[#"http://abcdef.xyz.com/Docs/NewFolder/myFile.docx" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setValue:[NSString stringWithFormat:#"bytes=%ld-",0] forHTTPHeaderField:#"Range"];
[req addValue: #"docx" forHTTPHeaderField:#"Content-Type"];
[req setHTTPMethod:#"POST"];
//---set the headers---
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
if (conn) {
NSLog(#"connection done ");
webData = [[NSMutableData data] init];
}
}
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:userCd password:passWord persistence:NSURLCredentialPersistenceNone];
NSLog(#"Crediantials done ");
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
NSError* error = [NSError errorWithDomain:#"SoapRequest" code:403 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: #"Could not authenticate this request", NSLocalizedDescriptionKey,nil]];
NSLog(#"Credentials are not valid");
[mydelegate loginFailled:false];
}
}
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
//[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data {
NSLog(#"recevied data %#",data);
webData=[NSMutableData dataWithData:data];
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection
didFailWithError:(NSError *) error {
[webData release];
[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"Did Finish Loading done ");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:#"FileName.docx"];
[webData writeToFile:pdfPath atomically:YES];
[connection release];
}
I just wrote a quick app to test using Apple's NSURLConnection which is both a simple and robust solution. I downloaded a few hundred KB Word and PowerPoint files. For authentication I used .htaccess. Works like a charm. Here is the relevant code.
- (IBAction)clickedDownload:(id)sender {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://myserver.com/downloadTest/testfile.pptx"]];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// inform the user
}
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:#"theUsername"
password:#"thePassword"
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
// inform the user that password is incorrect
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
receivedData = [[NSMutableData alloc] init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSURL *docDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *filePath = [docDirectory URLByAppendingPathComponent:#"testfile.pptx"];
[receivedData writeToURL:filePath atomically:YES];
[receivedData release];
connection = nil;
}
Now I opened the files in ~/Library/Application Support/iPhone Simulator/4.3.2/Applications/[unique-application-code]/Documents- works great!
You should look into ASIHTTPRequest.
http://allseeing-i.com/ASIHTTPRequest/How-to-use
NSURL *url = [NSURL URLWithString:#"http://abcdef.xyz.com/Docs/NewFolder/myFile.docx"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:#"username"];
[request setPassword:#"password"];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
- (void)requestDone:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
}
- (void)requestWentWrong:(ASIHTTPRequest *)request
{
NSError *error = [request error];
}
I was wondering if anyone can help me understand how to add SSL certificate handling to synchronous
connections to a https service.
I know how to do this with asynchronous connections but not synchronous.
NSString *URLpath = #"https://mydomain.com/";
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
NSMutableURLRequest *myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[myURLRequest setHTTPMethod:#"POST"];
NSString *httpBodystr = #"setting1=1";
[myURLRequest setHTTPBody:[httpBodystr dataUsingEncoding:NSUTF8StringEncoding]];
NSHTTPURLResponse* myURLResponse;
NSError* myError;
NSData* myDataResult = [NSURLConnection sendSynchronousRequest:myURLRequest returningResponse:&myURLResponse error:&myError];
//I guess I am meant to put some SSL handling code here
Thank you.
Using the static sendSynchronousRequest function is not posible, but i found an alternative.
First of all NSURLConnectionDataDelegate object like this one
FailCertificateDelegate.h
#interface FailCertificateDelegate : NSObject <NSURLConnectionDataDelegate>
#property(atomic,retain)NSCondition *downloaded;
#property(nonatomic,retain)NSData *dataDownloaded;
-(NSData *)getData;
#end
FailCertificateDelegate.m
#import "FailCertificateDelegate.h"
#implementation FailCertificateDelegate
#synthesize dataDownloaded,downloaded;
-(id)init{
self = [super init];
if (self){
dataDownloaded=nil;
downloaded=[[NSCondition alloc] init];
}
return self;
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace: (NSURLProtectionSpace *)protectionSpace {
NSLog(#"canAuthenticateAgainstProtectionSpace:");
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge {
NSLog(#"didReceiveAuthenticationChallenge:");
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[downloaded signal];
[downloaded unlock];
self.hasFinnishLoading = YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[dataDownloaded appendData:data];
[downloaded lock];
}
-(NSData *)getData{
if (!self.hasFinnishLoading){
[downloaded lock];
[downloaded wait];
[downloaded unlock];
}
return dataDownloaded;
}
#end
And for use it
FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init];
NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO];
[c setDelegateQueue:[[NSOperationQueue alloc] init]];
[c start];
NSData *d=[fcd getData];
Now you will have all benefits of have an async use of nsurlconnection and benefits of a simple sync connection, the thread will be blocked until you download all data on the delegate, but you could improve it adding some error control on FailCertificateDelegate class
EDIT: fix for big data. based on Nikolay DS comment. Thanks a lot
I had a similar issue. In my case i had an a-synchronous connection working with ssl as required using the two delegate methods that allowed me to accept any certificate:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
But i was stuck on doing the same in a synchronous manner. I searched the web until i found your post and unfortunately another stackoverflow post where it is hinted that you cannot perform synch calls on NSURLConnection and work with ssl (because of the lack of a delegate to handle the ssl authentication process).
What i ended up doing is getting ASIHTTPRequest and using that. It was painless to do and took me about an hour to set up and its working perfectly. here is how i use it.
+ (NSString *) getSynchronously:(NSDictionary *)parameters {
NSURL *url = [NSURL URLWithString:#"https://localhost:8443/MyApp/";
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
NSString *parameterJSONString = [parameters JSONRepresentation];
[request appendPostString:parameterJSONString];
[request addRequestHeader:#"User-Agent" value:#"MyAgent"];
request.timeOutSeconds = CONNECTION_TIME_OUT_INTERVAL;
[request setValidatesSecureCertificate:NO];
[request startSynchronous];
NSString *responseString = [request responseString];
if (request.error) {
NSLog(#"Server connection failed: %#", [request.error localizedDescription]);
} else {
NSLog(#"Server response: %#", responseString);
}
return responseString;
}
The important part of course is the
[request setValidatesSecureCertificate:NO];
Another alternative for you is to handle the download in another thread with an a-synch connection using the two methods above and block the thread from which you want the synch connection until the request is complete
Im close to finding the solution for this with the code below. This works but often crashes
probably because I am doing something wrong in the way I code this and I don't have a strong understanding of the methods used. But if anyone has any suggestions on how to improve this
than please post.
Just after the line:
NSError* myError;
and just before the line:
NSData* myDataResult = [NSURLConnection sendSynchronousRequest:myURLRequest
returningResponse:&myURLResponse error:&myError];
add:
int failureCount = 0;
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:#"mydomain.com" port:443 protocol:#"https" realm:nil
authenticationMethod:NSURLAuthenticationMethodServerTrust];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:myURL MIMEType:#"text/html"
expectedContentLength:-1 textEncodingName:nil];
NSURLAuthenticationChallenge *challange = [[NSURLAuthenticationChallenge alloc]
initWithProtectionSpace:protectionSpace proposedCredential:[NSURLCredential
credentialForTrust:protectionSpace.serverTrust] previousFailureCount:failureCount
failureResponse:response error:myError sender:nil];