Objective C: SSL Pining - objective-c

I have implement NSURLConnectionDataDelegate in my class and I have implemented the method:
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
But when I make new NSURLConnection this method isn't called. Why? Where is the problem? The connections is an SSL connection.
I have look inside an example project downloaded by internet (I don't remember where) and this project work fine, but if I do the some step in my project don't work. I need any external library or to set something in the proj? You can suggest me a for-dummies guide?
Edit 1:
In the example I use:
NSURL *httpsURL = [NSURL URLWithString:#"https://secure.skabber.com/json/"];
NSURLRequest *request2 = [NSURLRequest requestWithURL:httpsURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:15.0f];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request2 delegate:self];
[connection start];
But I want to work with any kind of NSURLConnection.
Edit 2:
This is my complete code:
#interface ConnectionDelegate : NSObject<NSURLConnectionDataDelegate>
#property (strong, nonatomic) NSURLConnection *connection;
#property (strong, nonatomic) NSMutableData *responseData;
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
-(void)testHTTPRequest;
#end
#implementation ConnectionDelegate
-(void)testHTTPRequest
{
NSURL *httpsURL = [NSURL URLWithString:#"https://secure.skabber.com/json/"];
NSURLRequest *request = [NSURLRequest requestWithURL:httpsURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:15.0f];
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
[self.connection start];
if ([self isSSLPinning]) {
[self printMessage:#"Making pinned request"];
}
else {
[self printMessage:#"Making non-pinned request"];
}
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
NSData *skabberCertData = [self skabberCert];
if ([remoteCertificateData isEqualToData:skabberCertData] || [self isSSLPinning] == NO) {
if ([self isSSLPinning] || [remoteCertificateData isEqualToData:skabberCertData]) {
[self printMessage:#"The server's certificate is the valid secure.skabber.com certificate. Allowing the request."];
}
else {
[self printMessage:#"The server's certificate does not match secure.skabber.com. Continuing anyway."];
}
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
else {
[self printMessage:#"The server's certificate does not match secure.skabber.com. Canceling the request."];
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (self.responseData == nil) {
self.responseData = [NSMutableData dataWithData:data];
}
else {
[self.responseData appendData:data];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *response = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
[self printMessage:response];
self.responseData = nil;
}
- (NSData *)skabberCert
{
NSString *cerPath = [[NSBundle mainBundle] pathForResource:#"secure.skabber.com" ofType:#"cer"];
return [NSData dataWithContentsOfFile:cerPath];
}
- (void)printMessage:(NSString *)message
{
Log(#"%#",message);
}
- (BOOL)isSSLPinning
{
NSString *envValue = [[[NSProcessInfo processInfo] environment] objectForKey:#"SSL_PINNING"];
return [envValue boolValue];
}
#end
If i put a breakpoint on connection:willSendRequestForAuthenticationChallenge: the program don't enter in it.

Related

NSURLConnection switch to NSURLSession returning 404 response

I am attempting to convert my project from using NSURLConnection to NSURLSession. However, after making the switch I cannot seem to get a response from the server. The returned response from the server is always 404.
My original code, using NSURLConnection:
#implementation RecorderManager
- (void)sendRequestToURL:(NSString*)url withData:(NSData*)postData forScreen:(NSString *)inScreenName
{
RecorderAsynchSubmit* delegate = [[RecorderAsynchSubmit alloc] initWithScreenName:inScreenName andURL:url];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
#implementation RecorderAsynchSubmit
// Implementing NSURLConnectionDelegate, NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Handle error
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[httpResponse setLength:0];
httpResponseCode = [((NSHTTPURLResponse *) response) statusCode];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)httpdata
{
[httpResponse appendData:httpdata];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseString = [[[NSString alloc] initWithData:httpResponse encoding:NSUTF8StringEncoding] copy];
if (httpResponseCode == STATUS_CODE_VALID) { // response code for entry submitted
responseString = #"Submit Succeeded";
} else {
responseString = [NSString stringWithFormat:#"Your entry could not be submitted. Data has been stored locally. Error Message: %#", responseString];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:#"submissionCompleteEvent"
object:[[RecorderNotificationMessage alloc] initWithStatusMessage:responseString detailedMessage:responseString]];
}
My updated code using NSURLSessionTask:
#implementation RecorderManager
- (void)sendRequestToURL:(NSString*)url withData:(NSData*)postData forScreen:(NSString *)inScreenName
{
RecorderAsynchSubmit* delegate = [[RecorderAsynchSubmit alloc] initWithScreenName:inScreenName andURL:url];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
NSURL *URL = [NSURL URLWithString:url];
NSURLSessionTask *task = [session dataTaskWithURL:URL];
[task resume];
}
#implementation RecorderAsynchSubmit
// Implementing NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
[httpResponse setLength:0];
httpResponseCode = [((NSHTTPURLResponse *) response) statusCode];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
[httpResponse appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
if (error) {
// Handle error
} else {
NSString *responseString = [[[NSString alloc] initWithData:httpResponse encoding:NSUTF8StringEncoding] copy];
if (httpResponseCode == STATUS_CODE_VALID) {
responseString = #"Submit Succeeded";
} else {
responseString = [NSString stringWithFormat:#"Your entry could not be submitted. Data has been stored locally. Error Message: %#", responseString];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:#"submissionCompleteEvent"
object:[[RecorderNotificationMessage alloc] initWithStatusMessage:responseString detailedMessage:responseString]];
}
}
The original code is working fine and returns the expected result, however the updated code always returns a 404 response from the server.

how can I use NSURLConnection Asynchronously?

I am using this code to load data to my App, can you tell me how can I make this asynchronously?
NSMutableURLRequest *request2 = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request2 delegate:self];
if (connection)
{
NSLog(#"NSURLConnection connection==true");
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request2 returningResponse:&response error:&err];
self.news =[NSJSONSerialization JSONObjectWithData:responseData options:nil error:nil];
NSLog(#"responseData: %#", self.news);
}
else
{
NSLog(#"NSURLConnection connection==false");
};
I think you should be bothered reading the documentation. There's a sendAsynchronousRequest:queue:completionHandler: method.
Create the connection with initWithRequest:delegate:startImmediately:, set yourself as its delegate and implement the delegate methods.
Block code is your friend. I have created a class which does this for you
Objective-C Block code. Create this class here
Interface class
#import <Foundation/Foundation.h>
#import "WebCall.h"
#interface WebCall : NSObject
{
void(^webCallDidFinish)(NSString *response);
}
#property (nonatomic, retain) NSMutableData *responseData;
-(void)setWebCallDidFinish:(void (^)(NSString *))wcdf;
-(void)webServiceCall :(NSString *)sURL_p : (NSMutableArray *)valueList_p : (NSMutableArray *)keyList_p;
#end
Implementation class
#import "WebCall.h"
#import "AppDelegate.h"
#implementation WebCall
#synthesize responseData;
-(void)setWebCallDidFinish:(void (^)(NSString *))wcdf
{
webCallDidFinish = [wcdf copy];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
NSLog(#"Response Code = %i", responseStatusCode);
if(responseStatusCode < 200 || responseStatusCode > 300)
{
webCallDidFinish(#"failure");
}
[responseData setLength:0];
}
- (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];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"WebCall Error: %#", error);
webCallDidFinish(#"failure");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *response = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
response = [response stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
webCallDidFinish(response);
}
-(void)webServiceCall :(NSString *)sURL_p : (NSMutableArray *)valueList_p : (NSMutableArray *)keyList_p
{
NSMutableString *sPost = [[NSMutableString alloc] init];
//If any variables need passed in - append them to the POST
//E.g. if keyList object is username and valueList object is adam will append like
//http://test.jsp?username=adam
if([valueList_p count] > 0)
{
for(int i = 0; i < [valueList_p count]; i++)
{
if(i == 0)
{
[sPost appendFormat:#"%#=%#", [valueList_p objectAtIndex:i],[keyList_p objectAtIndex:i]];
}
else
{
[sPost appendFormat:#"&%#=%#", [valueList_p objectAtIndex:i], [keyList_p objectAtIndex:i]];
}
}
}
NSData * postData = [sPost dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSString * postLength = [NSString stringWithFormat:#"%d",[postData length]];
NSURL * url = [NSURL URLWithString:sURL_p];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if (theConnection)
{
self.responseData = [NSMutableData data];
}
}
#end
Then you to make this web call, you call it like this
WebCall *webCall = [[WebCall alloc] init];
[webCall setWebCallDidFinish:^(NSString *response){
//This method is called as as soon as the web call is finished
NSString *trimmedString = [response stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if([trimmedString rangeOfString:#"failure"].location == NSNotFound)
{
//Successful web call
}
else
{
//If the webcall failed due to an error
}
}];
//Make web call here
[webCall webServiceCall:#"http://www.bbc.co.uk/" :nil :nil];
See the setWebCallDidFinish method, it will not be called until the webcall has finished.
Hope that helps!!
Here is some code which I am using in my app:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:yourURL]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"Error loading data from %#. Error Userinfo: %#",yourURL, [error userInfo]);
} else {
NSDictionary *dataFromServer = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
contentAsString = [[[dataFromServer objectForKey:#"page"] objectForKey:#"content"] stripHtml];
completionHandler(contentAsString);
}];
fyi: stripHTML is a NSString Category to remove HTML tags from JSON --> Found it Here
you can call your content in your class like that:
[yourClass getDataWithcompletionHandler:^(NSString *content) {
yourObject.content = content;
[yourClass saveManagedObjectContext];
}];
if you implement it once, you won't want to use synchronous connection again...
Check this out: HTTPCachedController
It will help you send POST and GET requests, while it will cache the response and after that it will return the cached data when no internet connection is available.
HTTPCachedController *ctrl = [[[HTTPCachedController alloc] initWithRequestType:1 andDelegate:self] autorelease];
[ctrl getRequestToURL:#"https://api.github.com/orgs/twitter/repos?page=1&per_page=10"];
You will get notified when the data are fetched through a delegate method:
-(void)connectionFinishedWithData:(NSString*)data andRequestType:(int)reqType

GHUnit test with async HTTP get action

I want to test async HTTP get action.
#interface Sender : NSObject<NSURLConnectionDataDelegate, NSURLConnectionDelegate>
{
NSMutableData *buffer;
NSString *_html
}
- (void)getHtml;
#end
#implementation Sender
- (void)getHtml
{
NSString *urlstr = #"http://www.yahoo.co.jp";
NSURL *url = [NSURL URLWithString:urlstr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
if (conn) {
buffer = [NSMutableData data];
} else {
// error handling
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[buffer appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeed!! Received %d bytes of data", [buffer length]);
_html = [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
NSLog(#"%#", _html);
}
#end
I have to let _html property ?
#import "SenderTestCase.h"
#import "Sender.h"
#implementation SenderTestCase
- (void)setUpClass
{
sender = [[Sender alloc] init];
}
- (void)testGetHtml
{
[self prepare];
[sender getHtml];
[self performSelector:#selector(_succeedGetHtml) withObject:nil afterDelay:3.0];
[self waitForStatus:kGHUnitWaitStatusSuccess timeout:4.0];
}
- (void)_succeedGetHtml
{
if (sender.html != nil) {
[self notify:kGHUnitWaitStatusSuccess forSelector:#selector(testGetHtml)];
};
}
#end
If there is more nice way, please tell me.
Thank you for your kindness.
You are doing it correct.
If you want your code to be nicer (and shorter), consider using AFNetworking and the use of blocks.
I have a GHUnit async test example, which shows nicely within 1 method.

How to return data directly which was loaded by NSURLConnection if delegate functions are needed?

A short explanation what I want to do: I'm using NSURLConnection to connect to a SSL webpage which is my API. The servers certificate is a self signed one so you have to accept it, for example in a web browser. I've found a solution on Stack Overflow how to do the trick (How to use NSURLConnection to connect with SSL for an untrusted cert?)
So I've added the NSURLConnection delegate to use methods like "didReceiveAuthenticationChallenge". As a result of that I cannot use this:
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
because there is no possibility to use the delegate functions in this case. My question is the following: I need a function which looks like this:
- (NSDictionary *)getData : (NSArray *)parameter {
[...|
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[...]
return myDictionary;
}
how can I return a NSDictionary by using this? As far as you know the delegate function of NSURLConnection are called now and the response isn't available at this point. The problem is that the view controller depends on this response so I need to return the dictionary directly... Does anybody know a solution for this? What about a callback function?
okay, I've found a solution for that. A very good thing is to use blocks in objective-c.
First of all you have to add some methods to NSURLRequest and NSURL:
#implementation NSURLRequest (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
FetchResponse *response = [[FetchResponse alloc] initWithBlock:block];
[[NSURLConnection connectionWithRequest:self delegate:response] start];
[response release];
}
#end
#implementation NSURL (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
[[NSURLRequest requestWithURL:self] fetchDataWithResponseBlock:block];
}
#end
And than just implement the follwing class:
#implementation FetchResponse
- (id)initWithBlock:(void(^)(FetchResponse *response))block {
if ((self = [super init])) {
_block = [block copy];
}
return self;
}
- (NSData *)data {
return _data;
}
- (NSURLResponse *)response {
return _response;
}
- (NSError *)error {
return _error;
}
- (NSInteger)statusCode {
if ([_response isKindOfClass:[NSHTTPURLResponse class]]) return [(NSHTTPURLResponse *)_response statusCode];
return 0;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_response = response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (!_data) _data = [[NSMutableData alloc] init];
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_block(self);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_error = error;
_block(self);
}
Now you can do the follwing, some kind of callback function:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://..."];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"application/json" forHTTPHeaderField:#"accept"];
[request fetchDataWithResponseBlock:^(FetchResponse *response) {
if (response.error || response.statusCode != 200)
NSLog(#"Error: %#", response.error);
else {
//use response.data
}
}];
Here you can find the orginal german solution by ICNH: Asynchrones I/O mit Bloecken
Thank you very much for this!
My suggestion would be to use some other delegate methods for NSURLConnection like connection:didReceiveResponse: or connection:didReceiveData:. You should probably keep a use a set up like so:
#interface MyClass : NSObject {
…
NSMutableData *responseData;
}
…
#end
- (void)startConnection {
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
responseData = [[NSMutableData data] retain];
} else {
// connection failed
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// connection could have been redirected, reset the data
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// connection is done, do what you want
………
// don't leak the connection or the response when you are done with them
[connection release];
[responseData release];
}
// for your authentication challenge
- (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])
if ([trustedHosts containsObject:challenge.protectionSpace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Get actual URL from TinyURL

I want the user to get the actual URL from the TinyURL or Tiny.cc services, or any other URL redirectors. So is it possible for me to get the actual long URLs from the short redirected URLs, without making a browswer application that runs in the background?
Thanks in advance.
Header:
#import "UntitledViewController.h"
#implementation UntitledViewController
- (id)init
{
self = [super init];
if (self)
{
NSURL *url = [NSURL URLWithString:#"http://tinyurl.com/a3cx"];
[self loadTinyURL:url];
}
return self;
}
- (void)loadTinyURL:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (!connection)
NSLog(#"could not connect with: %#", url);
}
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
int statusCode = [httpResponse statusCode];
// http statuscodes between 300 & 400 is a redirect ...
if (response && statusCode >= 300 && statusCode < 400)
NSLog(#"redirecting to : %#", [request URL]);
return request;
}
#end
Implementation:
//
// UntitledViewController.h
// Untitled
//
// Created by tushar chutani on 11-04-19.
// Copyright 2011 Fleetwood park secondary . All rights reserved.
//
#import <UIKit/UIKit.h>
#interface UntitledViewController : UIViewController {
}
- (void)loadTinyURL:(NSURL *)url;
#end
The NSURLConnection has a call back connection:willSendRequest:redirectResponse:. At this point you can inspect the redirectResponse to see where you are going.
UPDATE:
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
//Make sure tinyurl is doing the redirection
if([[[redirectResponse URL] host] compare:#"tinyurl.com"
options:NSCaseInsensitiveSearch] == NSOrderedSame)
{
NSLog(#"Redirect Location: %#", [request URL]);
}
//call [connection cancel]; to cancel the redirect and stop receiving data
//return nil; to cancel redirect but continue receiving data
//return request; will continue the redirection as normal
return request;
}
Check out the following code:
#import "TinyURLHandler.h"
#interface TinyURLHandler (Private)
- (void)loadTinyURL:(NSURL *)url;
#end
#implementation TinyURLHandler
- (id)init
{
self = [super init];
if (self)
{
NSURL *url = [NSURL URLWithString:#"http://tinyurl.com/a3cx"];
[self loadTinyURL:url];
}
return self;
}
- (void)loadTinyURL:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (!connection)
NSLog(#"could not connect with: %#", url);
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
int statusCode = [httpResponse statusCode];
NSLog(#"%d : %#", statusCode, [NSHTTPURLResponse
localizedStringForStatusCode:statusCode]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSLog(#"finished");
}
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
int statusCode = [httpResponse statusCode];
// http statuscodes between 300 & 400 is a redirect ...
if (response && statusCode >= 300 && statusCode < 400)
NSLog(#"redirecting to : %#", [request URL]);
return request;
}
#end
# openingsposter: I'm not 100% sure why you want to see the original project, I guess it's because you want to figure out what would be a good way to extract the final URL? Well, what I would suggest is you create a delegate and inform the caller once you got the final URL. If you need an example, I can add more source code ...