I'm looking for a way to use NSMutableURLRequest with app level proxyHost/Port settings, essentially a replacement for ASIHTTPRequest lib with proxyHost/proxyPort. I've tried modifying the CFReadStream (from NSURLRequest HTTPBodyStream), but it SIGSEGs when setting the proxy settings. I would rather not have to rewrite my app with CFNetworking, and it looks like AFNetwork lib doesn't include this feature yet either.
Has anyone successfully done this with NSMutableURLRequest?
The real answer appears to be creating a custom NSURLProtocol. Should be a straight forward derivation, and add the appropriate proxyHost/proxyPort to the request (along with any other values such as a customized User Agent string). Then, supposedly, all requests will be routed through this custom protocol (including UIWebView requests..direct or derived).
relevant samples:
https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013653
http://eng.42go.com/customizing-uiwebview-requests-with-nsurlprotocol/
I'll post more when I have the thing operational.
Things of note with this implementation.
Initially started using CFNetworking as the "wedge" in my custom
NSURLPRotocol, but quickly found I was rewriting the same code that
was in ASIHTTPRequest. So I just implemented the wedge with
ASIHTTPRequest.
The items that are not documented well (or at all), is the
interaction of UIWebView with NSURLProtocol callbacks, vs
NSURLRequest/Conenction with NSURLProtocol. Some Findings:
a) All dependent page resources are loaded automatically by UIWebView
(which we knew), and they all go through NSURLProtocol, so it is an
excellent place to put in code to modify all requests.
b) The UIWebView sets the Referer header. On a redirect, the only
way to get the UIWebView to update it's Referer from the original URL
to the new redirect URL is with the [[self client] URLProtocol:self
wasRedirectedToRequest:redirectRequest
redirectResponse:tmpHttpResponse]; callback.
c) when the above redirect callback is received by UIWebView, it
generates a new NSURLRequest (essentially the one you sent back to
it). So if you have a wedge that likes to do the redirect
internally, you have to cancel the Request that it attempts to make,
in favor of the new one from UIWebView.
You have to be careful with which callbacks you implement from ASIHTTPRequestDelegate. e.g. implementing didReceiveData will disable the built in gzip processing. willRedirectToURL disables most of the built in redirect processing (even if you call [request redirectToURL:newURL]; as recommended in the comments).
Related
I'm having some issues with video requests handled through a special protocol scheme in a NSURLProtocol subclass. Every other resource (images/text) are getting handled correctly, however, when a video request is sent, I only get a call to 'canInitWithRequest' and no follow up. So, my video resource doesn't get resolved. Now, I've looked around and I found no definite solution for this. Some people use instead an embedded HTTP server, but that seems an overkill. Does anyone know if this is a bug or if not, why is this limitation, is there an workaround for it?
A similar issue: Custom NSURLProtocol class for WebView doesn't work when loading video in HTML5 document , but unfortunately without an answer.
#Meda, I was facing the similar issue. Here what I found and hope it is useful to you.
I assume that you are using NSUrlProtocol because you want to intercept the video requests.
I was using web view which makes request for video over HTTP. It goes to NSURLProtocol and makes the request. When it receives the data, webView loads the video rendering plugin (looking at the mime type in HTTP header). The plugin needs the data to come as Partial HTTP response (Response code 206). Further, the plugin does not use NSURLProtocol class but uses network layer below it. So requests that plugin makes, do not go thru NSURLProtocol. Considering this, there could be 2 problems in your case.
1. HTTP server you are using may not be supporting partial responses.
2. HTTP server is not reachable directly (Can you access the video from safari or any other
browser on your device?)
You can verify both the cases by taking network trace. use tcpdump (available on Mac) to take network trace and see what is happening there.
I have seen all sorts of questions about modifying NSURLRequest but not NSHTTPURLResponse. Our scenario is that:
A web page that is loaded in a UIWebview will make an HTTP request. This is typically a form post in a hidden iFrame or AJAX request. All that we can control is the host name of the request. We cannot modify anything else in the webpage.
In the UIWebviewDelegate the request is captured. We extract some data out of the request URL. That data is processed and a result is generated.
Then I need to provide a custom NSHTTPURLResponse that includes the result from #2 to the web view.
This is similar to the basic functionality of a Java Servlet.
I have not found any way of being able to achieve #3.
Also UIWebviewDelegate doesn't seem to capture AJAX requests. There doesn't seem to be any workaround without placing custom java script in the web page as outlined here: UIWebViewDelegate not monitoring XMLHttpRequest?
Is there an alternative to ASIWebPageRequest in the ASIHTTPRequest library that I can use for downloading an entire web page in iOS including the CSS, JavaScript and image files, etc? I can't seem to find a similar class in the AFNetworking framework and so far my searches haven't been successful. I can't use ASIHTTPRequest as I can't seem to get it to work at all in any of my apps, no examples I've found work for iOS7 and I'd much rather use something more recent anyway.
I basically want to store an entire webpage locally in a directory on the iPhone/iPad so that a user can then edit it locally and send the whole directory to their web server later. The user also needs to be able to view the webpage at any time in a UIWebView.
If this isn't possible I'll have to just download the HTML file and then parse it to find the URLs of external resources, then download those separately. I'd much rather not do this, but if I have to then what's the best library for accomplishing this?
Thanks to anyone to helps me out!
It seems you are after something like wget but I'm not sure if you can do that in iOS.
You can use the native method NSString stringWithContentsOfURL:encoding:error: to fetch the HTML of a web page.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/clm/NSString/stringWithContentsOfURL:encoding:error:
You can intercept all of the HTTP requests made from your app via a custom NSURLProtocol. You'll have to expand on this concept, but here's a simple class that when dropped into your project will log out all of the requests being made (e.g. from a UIWebView).
#interface TSHttpLoggingURLProtocol : NSURLProtocol
#end
#implementation TSHttpLoggingURLProtocol
+ (void) load
{
[NSURLProtocol registerClass: self];
}
+ (BOOL) canInitWithRequest: (NSURLRequest *) request
{
NSLog( #"%#", request.URL );
return NO;
}
#end
You could expand on this and actually perform the requests from the protocol implementation (see https://github.com/rnapier/RNCachingURLProtocol for a more complete example). Or you could just track the requests and download them yourself later.
There's no easy way that I know of to associate a protocol handler like this with a particular instance of a UIWebView. You're going to intercept ALL of the requests in the app with this. (Caveat - in iOS7, protocols can be registered to specific NSURLSessions; for these I think a globally registered protocol handler won't intercept.)
I'd use a UIWebView and cache all Requests it makes
I actually have more than one question regarding this... I'm still new to NSURLCache and UIWebViews but somehow got the basics down I think.
Anyway, I was able to successfully implement Caching for my UIWebView(I think). I initialized the NSURLCache. My UIWebView loads its contents with the loadRequest: method. I assigned the ViewController that owns the UIWebView as it's delegate. I also had the ViewController implement NSURLConnectionDataDelegate. I implemented the connection:willCacheResponse: method just to log a message in the console so I could check that caching was done. I also logged the current disk and memory usage. It increases after the caching so I can see that it did work.
However, when I load the page again, I think the request is sent again to the server because the connection delegate methods like connection:didReceiveResponse: and connection:willCacheResponse are being called.
I also wanted to try getting the cachedResponse from the cache by using cachedResponseForRequest:. I called this method in webViewDidFinishLoad: but it always returns null. Did caching really work?
How can you tell if the WebView loaded from the cache? Am I misusing cachedResponseForRequest:? And on another note, what proper Control-Cache header values should be present in the very least for caching? I'm testing on google's homepage URL and it returns the private value for its Cache Control header but I can see that caching works because the connection:willCacheResponse: is called.
Help please?
If you run a HTTP proxy like Charles or Fiddler you can keep tabs on what network traffic is actually going out over the wire(less).
You can also inspect the request/response headers to see whether the response is indeed destined for the cache.
Note that in some cases you might see a conditional-GET (returns a 304) meaning the client asked for content that it has in its cache, but only wants to download the content if the server has a more recent version.
While testing my application, i connected to a wifi network which needed an authentication to access the internet.
I would have like [NSString stringWithContentOfUrl:encoding:error:] to fail or return the content of this authentication page even if it is not the page I asked. But it keeps on trying to download, and never returns.
Do you have any solution to detect this kind of issue ?
I would recommend using NSURLConnection. When a redirect happens it will call the delegate method as mentioned here. Also with NSURLConnection you will have greater control in the future when you add additional features and content. Or should the router not do a redirect and just force you to a page, you will be able to use the NSURLConnection to download the content and parse it to determine if it is indeed the page you were looking for.
In that case you'll have to do some coding. Download the contents of the url via NSURLRequest -> NSURLConnection. Then via the NSURLConnection's delegate methods you can respond to the authentication challenge.