URL reachablility - false positive - objective-c

There are a ton of "false positive" posts, though none appear to match my situation. (Most are talking about internet connection errors..)
As with everyone almost else, I just want to determine whether a specific URL is valid.
Now, I'm dealing with subdomains, and it works great as long as I choose a valid domain. If I choose an invalid subdomain, I get a false positive. Using a browser to test reveals that my ISP gives me a really annoying suggestions page if I type in a URL that doesn't exist.
Any way to validate that the responding server IS actually at the URL I asked for?
My Code:
-(BOOL) canConnectToURL:(NSString * )sURL {
BOOL bSuccess = NO;
if (![txtSubDomain.text.lowercaseString isEqual: #"www"]){
NSLog(#"canConnectToURL(%#)",sURL);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: sURL]];
[request setHTTPMethod: #"HEAD"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
NSLog(#"URL: %#",sURL);
NSLog(#"data: %#",response);
bSuccess = ([(NSHTTPURLResponse *)response statusCode] == 200);
}
return bSuccess;
}

I believe you can attach a delegate to your NSURLRequest that handles the method connection:willSendRequest:redirectResponse: in the NSURLConnectionDataDelegate protocol, and catch it there.
https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40011348-CH1-SW9

Related

Retrieve All Recordings on Twilio Using Programable Voice SDK

I'm attempting to download all Recording objects associated with a Twilio account but do not see any methods in TwilioVoiceClient.h or VoiceClient.h that achieve this. I'm using the ProgramableVoice iOS SDK (beta 5), but the online documentation doesn't show any objective-c or Swift. I'm able to playback individual recordings with no problems if the Recording SID is known like so:
NSString *baseString = #"https://api.twilio.com/2010-04-01/Accounts/";
NSString *recordingSID = #"RExxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *recordingUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#/Recordings/%#.mp3", baseString, kTwilAccount, recordingSID]];
avPlayer = [[AVPlayer alloc]initWithURL:recordingUrl];
What I'd like to do though, is download all associated recording objects for an account. For this, I've turned to NSUrlConnection:
NSString *baseStr = [NSString stringWithFormat:#"https://api.twilio.com/2010-04-01/Accounts/%#/Recordings.json", kTwilAccount];
NSURL *url = [NSURL URLWithString:baseStr];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1000.0];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", kTwilAccount, authToken];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Base %#", [authData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
[theRequest setValue:authValue forHTTPHeaderField:#"Authorization"];
[NSURLConnection sendAsynchronousRequest:theRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) {
if ([responseData length] > 0 && responseError == nil){
// do work
} else if ([responseData length] == 0 && responseError == nil){
NSLog(#"data error: %#", responseError);
} else if (responseError != nil && responseError.code == NSURLErrorTimedOut){
NSLog(#"data timeout: %ld", (long)NSURLErrorTimedOut);
} else if (responseError != nil){
NSLog(#"data download error: %#", responseError);
}
}];
This results in a download error with console output:
data download error: Error Domain=NSURLErrorDomain Code=-1012 "(null)" UserInfo={NSErrorFailingURLStringKey=https://api.twilio.com/2010-04-01/Accounts/ACdee27262ef5fd27a593a697d80e7f7b0/Recordings.json, NSUnderlyingError=0x17084aa70 {Error Domain=kCFErrorDomainCFNetwork Code=-1012 "(null)" UserInfo={_kCFURLErrorAuthFailedResponseKey={url = https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}}}, NSErrorFailingURLKey=https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}
Obviously, it either doesn't recognize the endpoint or doesn't like the way I'm authenticating. How should I be requesting this information in objective-c?
Thanks for reading.
Twilio developer evangelist here.
We actually don't recommend that you put your account credentials into the application and make API calls from your application. It would be possible for a malicious attacker to decompile your application, retrieve your credentials and abuse your account.
We recommend instead that you implement a server side solution to make the call to the API and retrieve your Recordings. Then you can communicate with your server from the application.
Here's a blog post that explains more and includes an example for sending an SMS from an iOS application.

AFNetworking JSON parsing - fails of unknown reason

im trying to parse some JSON. for simplicity ill explain using the default example at github:
when running:
NSURL *url = [NSURL URLWithString:#"http://httpbin.org/ip"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request success:^(
NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"IP Address: %#", [JSON valueForKeyPath:#"origin"]);
} failure:nil];
[operation start];
i get the correct output logged. however, when i copy the example's content (which is basically 1 element) to a txt or html file (so URLWithString gets #"http:// my server address /file.txt"), putting it on my testing server and trying to prase from there, i get no output. what is wrong with this? thanks for your time!
(note: if i go to http:// my server address /file.txt i can see the contents there clearly so that's not the problem)
edit: as suggested, the content is:
"{
"origin": "10.44.119.100"
}"
Your problem probably has something to do with the fact that you're serving content as a text file (.txt) rather than as JSON (Content-Type: application.json / .json extension). AFNetworking is strict about HTTP standards in order to guard against unexpected behavior. Either set the correct Content-Type header on your server, or (as a hack) do AFJSONRequestOperation +addAcceptableContentTypes: adding text/plain.
As a meta note: when asking a question on Stack Overflow, specifics matter. If you had posted the error you were seeing in the console, it would be much easier to determine what the problem was. Likewise, approximate code is not actual code; if you have a problem, be specific about exactly what's going on. Details matter.
You should encode the json data first and then write it into the text file and when you are reading the data from file... decode the data first...
EDIT:
replace JSON operation with simple http and check if you are able to get data from there...
and if you are then JSONOperation basically is seeking for json response which is not in text file... i guess
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setUploadProgressBlock:^(NSInteger bytesWritten,long long totalBytesWritten,long long totalBytesExpectedToWrite)
{
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *str = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#" Success %#",str);
// id response = AFJSONDecode(responseObject, nil);
[self requestSucceed:response];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"error: %#", operation.responseString);
}];

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

Constantly calling a method

So I have a method that checks for internet connection, but only during the -(id):init method. Can I set it up so that it constantly checks for connection? If it helps, here is the code:
- (id) checkConnected
{
NSError *error = nil;
NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"] encoding:NSASCIIStringEncoding error:&error];
if (URLString != NULL)
{
connected = YES;
}
else connected = NO;
if(connected == YES)
NSLog(#"Connected");
else if (connected == NO)
NSLog(#"NotConnected");
return self;
}
While Reachability is a good first-pass check as others have suggested, it only tests the negative case: is it impossible to make a connection? If a firewall is blocking you, or the remote server is down, or any of a thousand other things happens, Reachability might tell you a system is in principle reachable (i.e. you have a network connection and the host if routeable) but the host is not in fact reachable.
So for some applications what you are asking is not unreasonable. The thing you have to be careful about is not to block your main thread with constant tests. Here is some code that will repeatedly run tests in the background:
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
__block NSHTTPURLResponse *response = nil;
__block NSError *error = nil;
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(netQueue, ^{
while (! [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]) {
NSLog(#"Connection failed.");
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connection succeeded");
});
});
dispatch_release(netQueue);
Where "Connection succeeded" is logged you could instead write some main thread code that runs when a connection is successful. Note that I am passing in *response and *error from outside the block so they too will be available on your main thread inside or outside the block (assuming you keep them in scope) for your use.
You may want to throttle (i.e. just not use while()), but this is an implementation detail. Using NSTimer() as Richard suggested would work.
Finally, even with this code you still need to handle a potential failure of a subsequent connection. Just because it worked once doesn't mean the connection is available a millisecond later.

Xcode: Validate a URL before Loading it

Im having some trouble findig a way to validate a url on my app.
My intention is to load a URL and at the same time see if other webpage exist for example.
Load http://mysite.com/folder1/1.pdf
validate http://mysite.com/folder1/2.pdf
if folder1/2.pdf exists then load it, else validate /folder2/1.pdf
so far im loading the first pdf like this in order to be able to change the pdf number and the folder:
int numpag = 1;
NSString *baseUrl =#"http://www.cronica.com.mx/iphone/pdf_iphone/";
[pdfView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[baseUrl stringByAppendingFormat:#"folder1/%d.pdf", numpag]]]];
Thanks so much in advance!
how about this:
+ (BOOL)isValidURL:(NSURL*)url
{
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSHTTPURLResponse *res = nil;
NSError *err = nil;
[NSURLConnection sendSynchronousRequest:req returningResponse:&res error:&err];
return err!=nil && [res statusCode]!=404;
}
let me know if it works for you!
(keep in mind that this is a synchronous request and should not be executed on the main thread)
I had to change the line:
return err!=nil && [res statusCode]!=404;
to
return err==nil && [res statusCode]!=404;
for the correct Bool return. The error should remain nil.
This approach is NOT correct, You should avoid Synchronous calls as they are blocking.
Apple says: simply try and wait down to wait for response.