I'm trying to be able to catch a misspelled URL on a server.
I already connect to a server and download the URL for a video, however I wan't to make sure that the URL is valid, and there is actually a file at that location.
right now I have
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDownloadDestinationPath:path];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
[request setTimeOutSeconds:5.0];
[self.queue addOperation:request];
[self.queue go];
But requestWentWrong: doesn't seem to be called after a timeout, the app does nothing.
Before I put the timeout in, the request would attempt to download a file, and it would save it. So I know the timeout is working, I would just like to be able to display an error when It times out.
What am I missing?
Thanks.
Answered myself.
Used
[request setDidReceiveResponseHeadersSelector:#selector(requestStart:)];
And then checked the response code using [request responseStatusCode]
Which for my particular case I needed 301 (I was checking for mistakes in the URL)
Related
I develop an application that relies heavily on AFNetworking to load data from a private API into Mantle models. My API client is a singleton subclass of AFHTTPSessionManager.
I'm trying to implement a basic offline mode by leveraging NSURLCache. The approach is to modify the request's cache policy on dataTaskWithRequest:request:completionHandler to NSURLRequestReturnCacheDataDontLoad if the AFNetworkReachabilityManager says the network is not reachable.
While I can verify that this actually works by using breakpoints during tests, the requests themselves are not being cached. I verified this by downloading the application container from Xcode, and checking the Cache.db sqlite file, which was empty. I also verified that I was looking at the correct cache by manually creating a dummy NSCachedURLResponse and forcefully storing it in the cache using storeCachedResponse:forRequest. Then the dummy response did show up in the Cache.db sqlite file.
So, I think I narrowed down the problem to my "usual" responses not being cached. I can also modify the headers sent by the server API. What I intend to do here is set a header Cache-Control: private on the API responses (so as not to make the other clients that use this API cache responses they shouldn't), and modify this cache header in the block of setDataTaskWillCacheResponseBlock, but this block never fires.
I understand that the URL system may decide when to call URLSession:dataTask:willCacheResponse:completionHandler, which calls the block, based on some undocumented rules, mainly the presence of a Cache-Control header, and the response size/cache size ratio. But my response is a 145-byte JSON, and the Cache-Control header is set (I verified both via curl -v and inspecting the task parameter of the success block of my request.
I also tried a more aggressive cache header, Cache-Control: public, max-age=2592000, to see if this caches the response or at least calls the block, but the behaviour was exactly the same. I also checked the myAFHTTPSessionManagerSubclass.session.delegate property, which was indeed pointing to myAFHTTPSessionManagerSubclass, as expected.
I also tried overriding URLSession:dataTask:willCacheResponse:completionHandler directly in my subclass, but it still was not called. Setting a breakpoint on this same delegate method on AFURLSessionManager.m in the Pods directory also does not work, the execution never stops on the breakpoint.
I'm using version 2.5.4 of AFNetworking, according to my Podfile.lock file.
So, how to make my responses cache (preferably without setting agressive caching policies on the response), so I can implement a quick offline mode in my app?
EDIT: I also tried to create a NSURLSession to see if this would work. So, I created a simple Node.js server that just answers something simple and sets a cache header:
$ curl -v http://192.168.1.107:1337/
* Hostname was NOT found in DNS cache
* Trying 192.168.1.107...
* Connected to 192.168.1.107 (192.168.1.107) port 1337 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 192.168.1.107:1337
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Cache-Control: public
< Date: Thu, 11 Jun 2015 14:38:14 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
Hello World
And then created a simple view controller to test:
- (void)viewDidLoad {
[super viewDidLoad];
// Prime the cache
[NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:2*1024 diskCapacity:10*1024*1024 diskPath:#"mytestcache"]];
// The sleep is to be absolutely sure the cache did initialise
sleep(2);
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
[[session dataTaskWithURL:[NSURL URLWithString:#"http://192.168.1.107:1337"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"Got response: %#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}] resume];
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *))completionHandler {
completionHandler(proposedResponse);
}
The response is correctly printed, but willCacheResponse is never called (I set a breakpoint). I tried checking the container again, and the mytestcache directory was created, but it was empty. I also tried Cache-Control: max-age=86400, to the same result.
( cross-posted to AFNetworking issues at: https://github.com/AFNetworking/AFNetworking/issues/2780 )
Well, I will surprise you - the code you wrote above is working just fine, ...but it's incorrect for what you're trying to achieve ;)
Reason is simply - delegate methods are not being called when you create requests by method dataTaskWithRequest:completionHandler:. You need to use method dataTaskWithRequest: instead.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html#//apple_ref/doc/uid/TP40013509-SW1 - read first Note
Second thing is that NSURLSession just really sux when we talk about caching. You may face several issues in iOS 7.x/8.x. I faced such nightmare when I was implementing custom cache in my app, which eventually has nothing in common with NSURLCache. Also I decided to create my kind of framework based on NSURLSession and AFNetworking.
One of the issues on iOS 7 is creating NSURLSession with defaultConfiguration, the NSURLCache object will be different than global one - quite unexpected behaviour.
One of the issues on iOS 8 is that the POST requests are also being cached, which may compromise security of your app. BTW https://www.google.pl/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=nsurlcache+ios+8+broken
Maybe it does not answer your questions, but you should be aware of that strange behaviours and differences between iOS versions.
Honestly I recommend you to use AFHTTPRequestOperationManager instead of AFHTTPSessionManager, because at least NSURLConnection works like a harm with NSURLCache :)
I am trying to setup a http proxy server in my computer to simulate a virtual development environment.
To access the local url, I put the DNS info into computers' /etc/hosts.
Here is my test code:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:#"http://someurl.local/bluh/bluh/path"];
NSURLSessionDataTask *dataTask =
[session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(#"data is %# error is %#", data, error);
}];
[dataTask resume];
My Experiments:
1. Browsers like safari or chrome can access both local web services and remote web services.
2. NSURLSession will response a NSURLErrorDomain Code=-1001 "The request timed out." when access local urls.
3. NSURLSession works correct if url not in /etc/hosts.
I also tried the NSURLConnection, same output as NSURLSession's.
so, is it means the NSURLConnection/NSURLSession and browsers use a different strategy to do the DNS?
how to deal or bypass this?
update
I build this environment for ios 7.0+.
Environment:
computer system: OS X 10.9.4 (13E28) which set up a proxy and have server in.
device system: iOS 7.1.2(11D257)
.local is treated as a special TLD on OS X, and I think iOS. I believe this is primarily because it's used as an indicator for Bonjour lookups. Try changing to a different TLD for your development domains. I use ".localhost"; I've seen others use ".dev". Both of those work as expected.
It looks from the linked article that you might have been running into the problem specifically because you were using x.local—a single label, which will be looked up via Bonjour instead of by DNS, so you might also find that x.y.local works, too, but personally I'd just avoid .local all together.
So I want to download a file from a webserver even though the server response is 304 Not Modified.
I use the AFURLSessionManager's downloadTaskWithRequest: progress: destination: completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) method which works fine for files which aren't modified. However if the server response is 304, it seems there is no valid filePath in completionHandler block and the error says "Request failed: not modified (304)".
I tried configurating AFURLSessionManager's requestCachePolicy to NSURLRequestReloadIgnoringLocalAndRemoteCacheData but that doesn't help: although filePath is set, the data can't be reconstructed from this filepath.
Can someone help me by maybe specifying what exactly would be the best practice for downloading a file with AFNetworking and Server response 304. Thanks in advance!
Nevermind I found the problem. I don't have to set the requestCachePolicy of the NSURLSessionConfiguration. I always added IF_MODIFIED_SINCE_HEADER to the request parameter of downloadTaskWithRequest:. Omitting this, it works.
I am using AFNetworking to connect from an iOS v6.0 app to my local Cold Fusion 8 server, not on the same machine but on the same network and the connection times out. When I use an external public server it works fine.
I have tried the connection via IP address and it doesn't work.
I have tried an entry in the hosts file assigning a domain name to the IP address and this doesn't work either. Please see the error below.
I can however connect via a web browser just fine.
Also, the server side files are exactly the same. Versions of Cold Fusion are the same. The only difference that I can find are the public server is Win2003 with IIS6 and the local server is Windows7 with IIS7.
Any thoughts on why this would not work on a local network. Makes local development kind of difficult.
Here is the relevant code:
// load params
NSMutableDictionary *myParams = [[NSMutableDictionary alloc] init];
[myParams setValue:#"Hello" forKey:#"Parameter1"];
[myParams setValue:#"There" forKey:#"Parameter2"];
// Load the base URL into a URL object
// Changing this to an external public server works fine
NSURL *baseURL = [NSURL URLWithString:#"http://www.mylocalmachine.com/"];
// Create HTTP client and init with base url
AFHTTPClient *myHttpClient = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
// POST request to path with the parameters
NSMutableURLRequest *myRequest = [myHttpClient requestWithMethod:#"POST" path:#"myfile.cfm" parameters:myParams];
// Block response from the HTTP client request and pass it back to the calling object
AFJSONRequestOperation *myOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:myRequest
success:^(NSURLRequest *mySuccessRequest, NSHTTPURLResponse *mySuccessResponse, id mySuccessJSON)
{
// PROCESS THE SUCCESS RESPONSE;
}
failure:^(NSURLRequest *myFailureRequest, NSHTTPURLResponse *myFailureResponse, NSError *myFaliureError, id myFailureJSON)
{
// PROCESS THE FAILURE RESPONSE... AFTER 60 seconds the system will fallout to this block.
}];
// Create a queue object
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
// Add the operation object to the queue
[myQueue addOperation:myOperation];
The following is the error I get print the myFailureError object
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x8481000 {NSErrorFailingURLStringKey=http://www.mylocalmachine.int/myfile.cfm, NSErrorFailingURLKey=http://www.mylocalmachine.int/myfile.cfm, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x9189e20 "The request timed out."}
Update----
I believe I have narrowed the issue down to what I beleive to be an issue with the iPhone simulator accessing IIS 7. It will access previous versions of IIS on the same network using the same code no problem.
The problem may be in the iPhone Simulator's User-Agent. I have tried to find a way to change the iPhone Simulator's User-Agent or allow the User-Agent in IIS 7 but can not seem to figure it out. The User-Agent the iPhone Simulator 6 is presenting is (iPhone Simulator; ios 6.0; Scale/2.00).
Does anyone know how to either allow this User-Agent on IIS 7 or change the User-Agent in the iPhone simulator?
Has anyone else seen this issue?
Thanks in advance,
Ed
Update----
Hi Everyone
Ok so I figured out how to change the User-Agent and Content-Type using the NSMutableURLRequest. I changed these to match what the browser would send, FireFox browser, and tried again to no avail. I still believe there is an issue with the configuration of IIS 7.5 but I can not find it...
Thanks for anyones help!!!
Ed
please try to remove last 2 lines of code and write
remove this
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperation:myOperation];
add this instead
[myoperation start];
and also write failure:nil before that ending square bracket(]) like
AFJSONRequestOperation *myOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:myRequest
success:^(NSURLRequest *mySuccessRequest, NSHTTPURLResponse *mySuccessResponse, id mySuccessJSON)
{
// PROCESS THE SUCCESS RESPONSE;
}
failure:^(NSURLRequest *myFailureRequest, NSHTTPURLResponse *myFailureResponse, NSError *myFaliureError, id myFailureJSON)
{
// PROCESS THE FAILURE RESPONSE... AFTER 60 seconds the system will fallout to this block.
}failure:nil];
let me know is it working or not...!!!!
Happy Coding!!!!!!
I have resolved this issue. I will post what the resolution was to hopefully help someone else with a similar issue.
Turns out the problem was AVG installed on the system that was running IIS 7. It was a difficult issue to determine because any other system could access the web server with out any problems. It was only specific with the iPhone simulator accessing the system running IIS7. Even Safari or FireFox running on the same Mac would work just fine. Now what within AVG was causing the problem... That is yet to be determined.
I truly hope this helps someone along the way!
I am trying to upload a PNG file on FTP server using the following code.
NSURL *ftpURL=[NSURL URLWithString:#"ftp://username:password#localhost"];
NSMutableURLRequest *urlRequest=[[NSMutableURLRequest alloc] initWithURL:ftpURL];
[urlRequest setHTTPMethod:#"POST"];
[postData appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"imageUrl\"; filename=\"dummy.png\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[#"Content-Type: image/png\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[#"Content-Transfer-Encoding: binary\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
UIImage *img=[UIImage imageNamed:#"a.png"];
NSMutableData *postData=[[NSMutableData alloc] init];
NSData *data=UIImagePNGRepresentation(img);
[postData appendData:data];
[urlRequest setHTTPBody:postData];
[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:YES];
I don't receive any NSURL connection,like it has failed or ask for Authentication. Only, call back I receive is of
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
I am not sure whether NSURLConnection supports ftp upload. I am not looking to implement it using CFFTPStream. Can any one correct me in my approach?
I've had good success with BlackRacoon.
Uploading file over FTP will require using FTP commands like STOR or APPE. And FTP commands are not possible using NSURLConnection because NSURLConnection does not provide any way to send FTP commands. To do that, you'll have to use CFFTPStream.
The SimpleFTPSample sample code shows this.
https://developer.apple.com/library/ios/#samplecode/SimpleFTPSample/
Specifically, it shows download (via NSURLConnection and CFFTPStream), upload, directory listing, and directory creation (all via CFFTPStream).
Note: CFFTPStream has no public API for deleting files , although it is possible to delete files using the deprecated, but still functional, CFURLDestroyResource.
There is currently no way to send arbitrary FTP commands via CFFTPStream. If you need to do that, you'll have to create (or acquire) your own FTP library.
Your are trying to use FTP and POST in one go, which won't work in almost any case.
FTP is a complete different protocoll, see for example http://www.faqs.org/rfcs/rfc959.html
It is somewhat more interactive, however many ftp server have a http wrapper which allows easy download, but upload will remain special. So if you want to use it, you should take a library or CFFTPStream.