We've generally had success loading JSON-encoded data from our server using:
NSError* error;
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]
options:NSDataReadingUncached
error:&error];
(On a background thread to avoid blocking the main thread.) But, with increasing server load, we've recently seen an invalid but non-error (error == nil) server response of:
<html><body><script>document.cookie='ggggggg=00268082ggggggg_00268082;
path=/';window.location.href=window.location.href;</script></body></html>
A retry will often result in successful download of expected JSON-encoded data; the problem appears to be server-side. Three questions:
1) Does anyone recognize this server response?
2) Is our server attempting to create a cookie instead of returning our file/data!?
3) If so, where should we be looking to understand how to avoid this random cookie response from our server?
Based on message board traffic, we were able to confirm that the problem could be reproduced by a variety of means (app, browser, or command line) -- the issue had nothing to do with iOS or our application code. We were able to sporadically reproduce the problem from the command line with curl using the form:
curl -D - your-test-url-here -s
After further investigation by our ISP, they determined that the most likely cause of the invalid HTTP response was from their DDoS attack protection. After they reset the affected server(s), the problem was resolved.
Related
I have been using NSURLSession on my app since iOS 7 first came out. Recently some users started complaining that some features weren't working, after having a deep look at their logs I noticed that several network requests failed with error:
{NSLocalizedDescription=Server is unreachable. Check your network connection and try again., NSUnderlyingError=0x174c4c300 "The operation couldnât be completed. (NSURLErrorDomain error -1001.)"}
As I have no idea what the problem might be, I added some code to resolve the hostname when the requests failed:
NSString *urlString = [url host];
const char *hostUrl = [urlString UTF8String];
struct hostent *remote_entity = gethostbyname(host);
if (!remote_entity) {
DDLogWarn(#"DNS Lookup failed! Cant resolve Hostname: %# for request: %#", [url host], [url description]);
return nil;
}
else {
// Get address info from host entry
struct in_addr *remote_in_addr = (struct in_addr *) remote_entity->h_addr_list[0];
char *sremote_in_addr = inet_ntoa(*remote_in_addr);
NSString* hostIP = [NSString stringWithUTF8String:sremote_in_addr];
DDLogWarn(#"Hostname: %# IP: %# (original request: %#)", [url host], hostIP, [url description]);
return hostIP;
}
After deploying this change, I can see in my logs that even when the request fails ( with NSURLErrorDomain error -1001) I can still resolve the hostname.
I don't understand how this could happen, I can always resolve the hostname but the requests still fail somehow. Please note that I can't reproduce this and this bug only manifests from time to time with no predictable pattern.
Any ideas of how to proceed to fix this for good ? Should I implement a packet tracking mechanism in my apps (similar to traceroute on unix)? Could this be a misconfiguration on my servers since requests to other hosts (like Amazon S3) never fail? I'm using Heroku by the way.
Thanks in advance,
Ze
I recommend looking at Apple's Networking Overview Guide, specifically the Handling Network Problems Gracefully section.
When working with networking code, you have to account for the fact that sometimes things go wrong. This is especially true on mobile devices where cellular connections can come and go. In the above mentioned section on handling network problems, Apple makes recommendations for automatically retrying requests and detecting network changes using the SCNetworkReachability API. I recommend using that strategy in your networking code to give your users the best experience.
In your specific case, it appears the -1001 error is indicating a network timeout. The host lookup is succeeding, but for whatever reason, the network request did not make it to the server. In that case, a retry would make sense.
I have been stuck with this for a while and don't seem to get around this.
I am trying to read the contents of an URL as a string from an URL, But i get a weird
Error -> Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)"
My code :
fetchedString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"www.example.com/iphone"] encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#",fetchedString);
// if there is something wrong with the URL
if (error) {
NSLog(#"Error -> %#", error);
return ;
}
What am I doing wrong? I tried using getting as NSData as well, but I get null back.
Yes, the URL is missing the scheme: "http://".
"Error -> Error Domain=NSCocoaErrorDomain Code=256"
For the error code check the Apple documentation:
NSError codes in the Cocoa error domain.
NSFileReadUnknownError = 256,
NSFileReadUnknownError
"Read error, reason unknown"
Not that the error definition is very helpful. :-)
Also do not check if error is nil to determine if there is an error, check the return value for nil. error is not guaranteed to be nil on successful execution.
I had a similar problem accessing files located on my device. I followed NSURL isFileURL always returns NO
and used [NSURL fileURLWithPath] instead of [NSURL URLWithString] - this worked!
I got this error (Error Domain=NSCocoaErrorDomain Code=256) as soon as our ssl certificate expired. that may not help you but could help someone else.
Sandboxing
If you're using sandboxing in your app, you might want to check that com.apple.security.network.client is set to YES. It's in the the General tab of your Target in Xcode 5 under
Network: Outgoing Connections (Client)
Also be aware that if you see a code 257 when trying to reach a file:/// url, that's also probably because of sandboxing, but this time rather the File Access part. Because I didn't want to open it to anything else than `com.apple.security.files.user-selected.read-write'
User selected files
I preferred to use A Dead Simple Fileserver and use http://localhost:3000 when in Debug mode.
More reasons that might be causing this specific error:
SSL is misconfigured on the server
The server redirects (301) the http URL to https (see #1)
App transport security also uses this code for blocked requests.
I got the same error. The above marked answer is perfect. But in my case, I had the "http://" in the url but had to add the port number in the url request since there is a service running on a specific port that is actually responding to your request.
#"http://example.com:8084/yyy.zzz"
I got the same error, the above solution didn't work for me, in my case i was calling dataWithContentsOfURL from within a UNNotificationServiceExtension so i had to update the info.plist file of the UNNotificationServiceExtension with the app transport security entries.
I'm working on a program for the iPhone and I ran into a bit of a snafu. I want to load a URL into a WebView, however, I want to make sure that the URL actually exists and is working before trying to load it into the view.
Imagine you have 200 servers and you know your file exists you just don't know which server. It is at http://serverXXX.mydomain.com/myfile.html where XXX is the server # 0 through 200
I wrote a bash script that uses cURL to loop through the servers checking the HEAD request of each iteration of the url and timing out after 1 second:
http://server1.mydomain.com/myfile.html
http://server2.mydomain.com/myfile.html
...
http://server199.mydomain.com/myfile.html
http://server200.mydomain.com/myfile.html
When it gets back a response it greps the response and compares it. If the response is OK it loads the URL in an external program and exits the script. How do I do this in Objective-C without the error "The requested URL was not found on this server." popping up?? I don't want to click OK 200 times while it tries to find the correct URL.
You can do the same as your script with an instance of NSMutableURLRequest and a NSURLConnection. You can set the request to HEAD (setHTTPMethod:) and iterate over the server URLs updating the request after each response (connection:didReceiveResponse:) is received.
Several times before I used the method...
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
to send/receive data synchronously when in an NSOperation and it has worked.
I'm working on a new project (new API, new server) now and the error just isn't being populated.
I'm using Charles proxy to snoop the network traffic and I can see "Response Code 500 Internal Server Error" coming back from the failed request in Charles but in the app the error object is always nil.
Like I said, I've used this loads before and the error is always populated if an error occurs.
Is there something on the server side that I can point out to the server side dev? He's adamant that I must be doing something wrong. The only thing I don't understand is that Charles is picking up response code 500 from it.
N.B. The problem is not that the error is occurring. The problem is that when it does occur the NSError object is not reporting it back to me. Just to clear it up in case anyone tells me to ask the server dev to fix the error. :-)
Your error object is not populated because the request succeeded from NSURLConnection's perspective -- that is, it was able to send data to the server and receive a response.
For protocol-specific errors, you need to inspect the NSURLResponse you get back. In this case, cast the response to NSHTTPURLResponse and invoke -statusCode.
The error is only populated if there is no response from the server or not a valid HTTP response, e.g. connection loss etc.
"Response Code 500 Internal Server Error" means there was an internal server error and your server returns an HTTP message with the response code 500. You should use NSHTTPURLResponse instead of NSURLResponse and call the statusCode method to check the HTTP response code. If the response code starts with 2 everything is fine. So I usually have a check like this: statusCode / 100 == 2.
For more HTTP response codes see http://en.wikipedia.org/w/index.php?title=HTTP_response_codes
I've encountered a problem using AFNetworking to download files from a Rackspace repository.
Basically sometimes the file is not completelly transfered but AFNetworking fires the success block. Also the http response code received is set to 200 OK.
This situation only happens on a 3G connection. On WiFi we correctly receive all the files.
Can anyone give us any hints on what can be causing this behaviour?
Edit, 2012-05-10
I've detected that the problem may be elsewhere, what I am doing is checking the CRC of the file and compare it to the expected CRC. However (and it only happens when the connection is over 3G) the CRC check fails. Below are some snippets of the code I'm using to download the file and then check it's CRC:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlNS];
AFImageRequestOperation * imageRequestOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request
imageProcessingBlock:nil
cacheName: nil
success: ^(NSURLRequest * request, NSHTTPURLResponse * response, UIImage * image)
{
[self.class postProcessDownloadAfNetworking: request andResponse: response resultImage: image error: nil];
}
failure: ^(NSURLRequest * request, NSHTTPURLResponse * response, NSError * error)
{
[self.class postProcessDownloadAfNetworking: request andResponse: response resultImage: nil error: error];
}];
imageRequestOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:obj.destinationFilename append:NO];
[imageRequestOperation start];
And then in the callback method:
if(error == nil)
{
NSData * data = [NSData dataWithContentsOfFile:imgData.destinationFilename];
uint32_t fileCrcInt = [data crc32];
NSString * fileCrcHex = [NSString stringWithFormat:#"%2X", (int) fileCrcInt] ;
NSString * fileCrcHex2 = [NSString stringWithFormat:#"000000%#", fileCrcHex] ;
fileCrcHex2 = [fileCrcHex2 substringFromIndex:[fileCrcHex2 length]-8];
// Compare CRC
if( [expectedCRC isEqualToString:fileCrcHex2] )
{
(...)
Is it possible that the instruction [NSData dataWithContentsOfFile:] is reading the file before the Output Stream from AFNetworking writes it completely? Maybe I'm encountering some kind of iOS disk cache or something like that.
In the absence of other data, I'll take a wild guess:
Certain file types may be subject to a transparent proxy over 3G, which may alter those files. This is with widely-recognized file types with lossy compression. Basically, if you're trying to download a JPEG the proxy might deliver a smaller JPEG than you expect. The cell provider considers it "close enough", and the doubly-compressed JPEG is smaller (and far uglier). Naturally, the re-compressed image will have a different CRC.
This may be true of other file types as well.
I have read reports of T-Mobile doing this. I think it's been noted with other cell providers, too.
There's two possible fixes:
As noted by Tony Million in a comment below, switching to HTTPS will fix this problem as the proxy will no longer be in the middle of the communication.
If your resource is available via HTTPS, this is a fantastic and simple fix.
If not, you can try adding a HTTP header intended to defeat this. I can't verify it will work for you, as my cell provider doesn't play this particular game:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5
The idea is to add a no-transform cache-control HTTP header to your request. This should disallow the server from making this kind of change.
This would seem to be a Rackspace / server issue. If the server is sending back a 200 response code, then that indicates a successful connection and file/data transfer.
Without more details on the server side of things, we can't really help ya.