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.
Related
If you are serious about answering this question, please clone the mini app i have created and see if it misbehaves for you the same way, before speculating about the answer, thank you :)
https://github.com/pavel-zdenek/nsurlprotocol-testbed
A very simple browser: UIWebView with all requests going through bare bones NSURLProtocol reimplementation. It elapses the loading time. The switch un/registers the protocol handler at runtime. Now load one specific site: http://www.rollingstone.com. Besides tons of other resources, the page GETs
http://sjc.ads.nexage.com/js/admax/admax_api.js
which produces an XHR request for
http://sjc.mediation.nexage.net/admax/adMaxApi.do?dcn=<somehash>
PZProtocolHandler OFF: XHR loads instantly. Observed as HTTP 200 on the wire(shark), reflected as successful load in Safari Web Inspector. On further loads, it's not on the wire anymore but still HTTP 200 in Web Inspector. In other words, cached.
PZProtocolHandler ON: Web Inspector reports 3 tries, timing out after 10s each, resulting in HTTP 404. No change with further reloads. Now the interesting part: according to wire(shark), the request goes out and is responded with proper HTTP 200. Furthermore, all 3 tries are sequenced on one and the same TCP channel. "Someone" is making up 404 along the way to UIWebView.
Now, even before reading clever blogs, i found out that iOS URL caching goes thermonuclear when the server response does not contain Cache-Control header. And this particular GET request is such case. But it should not result in 404 failure, should it? Also i have tried to hibernate the default caching through various methods: implementing caching-related delegate calls in NSURLProtocol, creating new request with cache prohibiting flags, using expectedly top knowledge in SDURLCache, even replacing NSURLCache with what i think should be a "null cache". See everything in the project sources.
The 404 persists for me. I doubt that the mere fact of overriding NSURLProtocol would break things so much. I could live without an opportunity to fill a radar for that, thank you. I still hope that i am doing something wrong.
The issue isn't exactly solved, more like narrowed and cleared a little bit. If a new question develops, it will be most probably very different, so i'm self-answering this one.
So it probably isn't a cache issue. The site loads normally when explicit NSOperationQueue is assigned:
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO];
// comment out for sure stalling
[_connection setDelegateQueue:[NSOperationQueue mainQueue]];
[_connection start];
I would be tempted to prefer currentQueue, but that's nil at the moment of [NSURLProtocol startLoading]. Which according to Apple doc should mean that startLoading isn't a NSOperation itself, hence there is no queue.
Explicit routing to main thread obviously brings a different performance bottleneck: the delegate calls are stalled when UI is being interacted with, most exemplary when partially loaded page is attempted to be scrolled. While this is also an issue, it's something specific i can fill Apple TSI with. Any new knowledge will go to the github repo.
UPDATE:
Now the issue is fully solved by doing yet another simple thing: create a new vanilla NSOperationQueue and set the NSURLConnectionDelegate queue to it:
[_connection setDelegateQueue:_theNewlyCreatedQueue];
You aren't going to see this in any of the numerous NSURLProtocol howtos on the internets. It would be inappropriately self confident to claim that i know why it improves things so much, but now the offending website loads almost as smoothly as in Mobile Safari (within expected JavaScript execution speed difference) so the problem is solved for me.
Currently i have a wepage where I am sending a ajax request from javascript and in respose for that server is sending a video file which will be by default saved browser download location. I want the user to select download path each and every time for the file download(which can be achieved by changing browser settings which is not suited for me). So i want to include a activex object which can send ajax request and get its response. First I want to know whether is it possible, if yes is there any prototype/examples, or please let me know how it can be achieved.
It is possible; FireBreath has a mechanism called BrowserStreams that would probably work for what you're describing, but honestly I'd suggest against it. See if you can do what you need using an extension; Chrome is dropping support for NPAPI next year and even if they weren't I think it's a really bad idea to use a plugin for something like this.
Up to you, of course. There are examples for making HTTP GET and POST requests in the FBTestPlugin example in FireBreath.
I'm trying to make a UIWebView application, just like any other (with refresh, go forward, back, google search, etc). It is going to be very simple. One thing I want to do however, is make the data loaded into the UIWebView loaded through a proxy server (like hideMyAss) - so websites like at schools or workplaces become unblocked.
I have been looking for a proxy which enables me to input the website address at the end of a proxy's URL, but I have not found one.
E.g. Hidemyass.com?url=google.com
Does Apple have any documentation as to how I could achieve this. I have no idea where to start looking as I don't know the exact name of what if be looking for. Any suggestions would be really helpful. Thanks!
Create a subclass of NSURLProtocol class that will handle all web protocols such as HTTP, HTTPS, SSL etc. This is a abstract class that provides the basic structure for performing protocol-specific loading of URL data. Guide on NSURLProtocol
Once your created your custom url protocol handler, register it in your appDelegate so your protocol will have priority over any of the built-in protocols.
[NSURLProtocol registerClass:[MyURLProtocol class]];
In terms of proxy, create your own server and implement a ready made solution that will do all your tunneling of client data to outside world. Tinyproxy is a example of a free software that can do your proxy requirements, research others or even create your own solution if you got the time.
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.
How do I play a track from a SoundCloud URL, which, for example, I got from the xml response from a query
<stream-url>https://api.soundcloud.com/tracks/31164607/stream</stream-url>
I should have thought that it would have been as easy as:
https://api.soundcloud.com/tracks/31164607/stream&client_id=my_client_id
yet I get
<error>401 - Unauthorized</error>
All I want to do is consume it in a Silverlight MediaElement, so all I need is set some url to the MediaElement's Source property.
I've checked an application that I wrote about 2 years ago, and THEN, accessing the stream url was as easy as this for a public track:
http://api.soundcloud.com/tracks/18163056/stream&consumer_key=MY_CONSUMER_KEY
however this no longer seems to work.
For example, all I had to do then in C# was:
MediaElement me = new MediaElement();
me.Source= new Url("http://api.soundcloud.com/tracks/18163056/stream&consumer_key=MY_CONSUMER_KEY");
me.Play();
Any hints would be appreciated.
I had a reply on a Microsoft forum that seems to imply that SoundCloud might not be possible to stream to Windows 8 Metro devices without consuming the whole stream before playback starts - which is quite worrying and would seem to imply that to make authentication possible, it would have to be done entirely in the url querystring insterad of using the header:
(The following reply is the answer to the following question: 'I am able to access an audio stream by http using the MediaElement, however I need to access it via https in which I need to add the oAuth info to the header of the initial request.
How is this done when using a MediaElement, and if it cannot be done, what is the workaround for consuming an audio feed in Metro 8 that requires header authentication to stream?')
"Direct access to the underlying network stream is not currently permitted by the MediaElement. Because of this there is currently no way to modify the header of the HTTP request to include any additional authentication information. That said, you do have control over the URL. You could theoretically setup an HTTP proxy service that translated the HTTP GET request parameters into the necessary oAuth credentials. Keep in mind that this is just a theoretical workaround. You may find different behavior in practice. Another theoretical workaround would be to handle the oAuth yourself via a raw stream socket and pass the retuned media data to the MediaElement via "Set Source" and a "Random Access Stream". Please keep in mind that this method has major limitations. in order to use a "Random Access Stream" with the ME you need to make sure all of the data is available before passing it to the ME."
The proxy service is not scalable for an application that is merely distributed for free as every stream would need to come via the proxy. And the raw stream socket, although getting around this, would mean that playback could not start until the whole file had downloaded - and this goes against all current UX (User Experience) guidelines.
So once again, if anyone has any tips, or info about how the whole authentication thing can be achieved in a querystring instead of using headers, I'd appreciate it!
I'm a little confused about whether you're referring to a public or a private track? If it's a public track, then you shouldn't need to send any authentication information, just your client id.
When I request https://api.soundcloud.com/tracks/31164607/stream?client_id=YOUR_CLIENT_ID then I get a 302 redirect to the proper mp3 stream.
Remember, adding parameters to a URL must start with a ? not &. This could (more than likely) be the reason why you are getting a 401 (SC is not picking up the client_id).
After authentication the link like this
http://api.soundcloud.com/tracks/103229681/stream?consumer_key=d61f17a08f86bfb1dea28539908bc9bf
is working fine. I am using Action Script.
I'm following up on Tom's reply because he calls attention to url character specificity. My HTTP requests randomly started failing today, and I was prefacing my client_Id with a ?. As soon as I changed that single ? to &, it started working. So in my case, SC wasn't picking up my client_Id because I used the wrong character. I think depending on where in the request we're talking about specifically, it's worth noting that differences between ? and & do make a difference.