19

Can anyone tell me the way how I can make a synchronous call to the https server? I am able to do asynchronous request on https server using following delegate methods.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace

and

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

but I need to do synchronous.

Anis
  • 205
  • 1
  • 3
  • 7

3 Answers3

24

//Encoding the request

NSData *postData = [xmlText dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

**//Calculating length of request**
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:requestUrlString]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/xml" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
NSURLResponse* response;
NSError* error = nil;

//Capturing server response
NSData* result = [NSURLConnection sendSynchronousRequest:request  returningResponse:&response error:&error];
Snehal
  • 597
  • 3
  • 10
  • 2
    Use of NSUTF8StringEncoding should be preferred if there is data that also has international character support, e.g: German, italian, etc.. Else the above method is absolutely perfect.. – Apple_iOS0304 Oct 09 '12 at 10:32
  • Don't forget the emoticons, they need UTF8 too ;) – Julian F. Weinert Jun 18 '13 at 11:31
  • 1
    The guy needs an instance dependant function call in order to provide the delegate to respond to the https challenge – LolaRun Jul 01 '13 at 09:47
13
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

in NSUrlConnection should work just fine with https.

If you'd like to provide credentials, they need to be part of the url: (https://username:password@domain.tld/api/user.json).

There's no way to provide a NSURLConnection delegate, so if you need some nonstandard authentication handling you'll need to do it asynchronously.

anq
  • 3,102
  • 21
  • 16
  • 1
    The guy needs an instance dependant function call in order to provide the delegate to respond to the https challenge – LolaRun Jul 01 '13 at 09:48
6

That's how i did it: instead of

[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]

I made the same method instance based, on the containing class, since we will need a delegate. And don't make it singleton, so every connection has its independent variables, because, if we don't, and two connections happen to be called before the other finishes, then the received data and the handling of the loops will be intertwined irrecoverably.

[[ClassNameHere new] sendSynchronousRequest:request returningResponse:&response error:&error]

This way i can create an NSUrl connection and handle it (in a synchronous way, we'll see how) so i don't have to change any of the previously written code.

- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse *__strong*)response error:(NSError *__strong*)error
{
    _finishedLoading=NO;
    _receivedData=[NSMutableData new];
    _error=error;
    _response=response;

    NSURLConnection*con=[NSURLConnection connectionWithRequest:request delegate:self];
    [con start];
    CFRunLoopRun();

    return _receivedData;
}


- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
{
    //handle the challenge
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    *_response=response;
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receivedData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    *_error=error;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    CFRunLoopStop(CFRunLoopGetCurrent());
}

The trick was in the CFRunLoopRun() and CFRunLoopStop(CFRunLoopGetCurrent()) I hope it helps someone else in the futur.

LolaRun
  • 5,526
  • 6
  • 33
  • 45
  • 2
    I won't ask why one would answer a 250 years old question, and especially that one ;) But more importantly, I would like to mention that your code does not authenticate properly, and is not secure. So, if this is just test code in order to get rid of certificates, this is OK. For production, its a security risk. – CouchDeveloper Jul 01 '13 at 19:23
  • Here i faced the same problem as he did, i had to connect to an https later in the development. And i already had the data layer using synchronous request. And to adapt to the server challenge we had to make it asynchronous, and it would have been cumbersome to remake the data layer to synchronous. This code actually handles the authentication in a synchronous asynchronous :) way. Thus the data(model) layer is still intact perfectly, with no modifications at all in it. – LolaRun Jul 03 '13 at 16:16
  • Now concerning the security of the challenge handling. I read this artile: https://developer.apple.com/library/ios/#technotes/tn2232/_index.html#//apple_ref/doc/uid/DTS40012884-CH1-SECRESOLVINGFAILURES And my case is not where my server has a Verified Certificate, it was self signed, and it will stay that way. And no parent CA for the server cert. So either this, or download the certificate and compare the data. And i think both approaches, have the same vulnerability of attack. So we have to work with the server to make more secure. – LolaRun Jul 03 '13 at 16:21
  • But you're right, putting this here, might make other people using it, and is not the optimum of security, and they might not have my case of a server self-signed cert. So thank you for your goodwill and your sharp eye. I'll make the handling of the challenge ambiguous. Regards :) – LolaRun Jul 03 '13 at 16:22
  • 1
    Validating a certificate with a root CA is preferred. Self signed certificates may not be that secure and have other problems (when they get updated, you need to update your client, too). You may still create this CA yourself - you don't always need a trusted third party which is costly. I've posted an answer how to [verify a self-sigend certificate](http://stackoverflow.com/questions/17141226/create-ssl-connection-using-certificate/17146532#17146532) – CouchDeveloper Jul 03 '13 at 16:37
  • yes, your answer uses the certs data comparison. But that's not creating a root CA right? Thanks anyway – LolaRun Jul 04 '13 at 13:06
  • 1
    Yes, that's true :/ You would need to set the anchor and then evaluate. Creating a CA and certificates is a different topic, though. You may find this [gist](https://gist.github.com/igalic/4943106) useful . – CouchDeveloper Jul 04 '13 at 13:42