Bug 138169 - WKWebView does not fully support custom NSURLProtocol
Summary: WKWebView does not fully support custom NSURLProtocol
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: 528+ (Nightly build)
Hardware: iPhone / iPad Unspecified
: P2 Blocker
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2014-10-29 04:02 PDT by Carlos Lozano
Modified: 2017-06-08 19:41 PDT (History)
34 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Carlos Lozano 2014-10-29 04:02:25 PDT
Summary:
It was possible to install a custom NSURLProtocol to handle requests from UIWebView. This is no longer possible with WKWebView - the NSURLProtocol subclass *does* receive the +canInitWithRequest: message, but is never instantiated. 


Steps to Reproduce:
1. Create and register a custom NSURLProtocol subclass that handles all requests.
2. Create a WKWebView and load a request.


Expected Results:
The custom NSURLProtocol subclass should receive a +canInitWithRequest: message, and then an instance should be created and initialized with -initWithRequest:cachedResponse:client:.


Actual Results:
The NSURLProtocol subclass *does* receive the +canInitWithRequest: message, but is never instantiated.


Version:
iOS 8.0, iOS 8.0.1, iOS 8.1 (tested)


Notes:
This breaks any behind the scenes encryption, compression, interception, caching, etc. This is breaking many things and blocking the adoption of WKWebView.


Configuration:
All emulators and devices with >= iOS 8 support.
Comment 1 Trevor Linton 2015-05-25 13:01:52 PDT
As a side note, this is also an issue with WKWebView on OSX Yosemite 10.10.3, the circumstances are exactly the same it seems as with iOS.
Comment 2 Radar WebKit Bug Importer 2015-06-11 16:29:36 PDT
<rdar://problem/21349577>
Comment 3 Justin Caldicott 2015-12-02 02:22:52 PST
I'm surprised this bug has so little activity.

We would love to be able to use NSUrlProtocol with WKWebView (on OS X).  We'd be able to avoid having a local web server running - less resources used and better performance.
Comment 4 Erwin van Dijk 2016-05-19 08:36:18 PDT
Are there any plans on implementing this functionality? I'd really love to use WKWebView, but this limitation forces me to keep using the UIWebView.
Comment 5 Sam Weinig 2016-05-22 23:22:25 PDT
(In reply to comment #4)
> Are there any plans on implementing this functionality? I'd really love to
> use WKWebView, but this limitation forces me to keep using the UIWebView.

We are investigating it, but it would really help us to understand more about why you were using this functionality. What type of loads did you need to intercept?
Comment 6 Ignacio Calderon 2016-05-23 00:22:50 PDT
In my particular case, this interception is needed for 2 main purposes:
1. Implement SSL pinning for web view content (most important)
2. Provide encrypted on-device assets, and be able to decrypt them before pass them to the web view for rendering.
Comment 7 Erwin van Dijk 2016-05-23 01:41:10 PDT
>We are investigating it, but it would really help us to understand more about why you were using this functionality. What type of loads did you need to intercept?

We are loading binary files which are transformed into multiple images. Default browser cache does not provide what we want. We want to be able to use local stored files if available or download them from a remote server. Therefore we need to intercept ajax calls to specific urls.
Comment 8 Pavel Zdenek 2016-05-23 04:21:42 PDT
(In reply to comment #5)
> (In reply to comment #4)
> > Are there any plans on implementing this functionality? I'd really love to
> > use WKWebView, but this limitation forces me to keep using the UIWebView.
> 
> We are investigating it, but it would really help us to understand more
> about why you were using this functionality. What type of loads did you need
> to intercept?

Glad to see that someone at Apple is picking this up. We even spent a TSI on asking this a long time ago. The anwser was basically "not considered a bug" so we shrugged and keep dragging the UIWebView cart.

We have built a web browser which allows for configurable intervention in the complete webview traffic, i.e. not just frames but all resources. Even XHR. The possible interventions are numerous. Cancel the request altogether, redirect, fake a response (e.g. a locally made image), tinker with outgoing or even incoming HTTP headers. It all works quite nicely on UIWebView, but it feels being left behind indeed.
Comment 9 Stefan Arentz 2016-05-23 05:30:07 PDT
(In reply to comment #5)
> (In reply to comment #4)
> > Are there any plans on implementing this functionality? I'd really love to
> > use WKWebView, but this limitation forces me to keep using the UIWebView.
> 
> We are investigating it, but it would really help us to understand more
> about why you were using this functionality. What type of loads did you need
> to intercept?

Some things we would like to do with WKWebView that are currently impossible due to no ability to intercept and modify loading behaviour:

* HTTPS Everywhere - Detect HTTP loads of resources in a whitelist, modify them to become HTTPS loads
* Content blocking - Block resources, both top level and resource loads
* Better control over headers - Ability to look and modify headers, both incoming and outgoing
* Implement custom content caching and content preloading - Need the ability to take over a resource load and provide our own response
* Better control of cookies. Currently there is no way to control cookie policy in WKWebView, this is trivial to imlement if we can control network requests

Giving developers access to the URL loading system opens the door to work around many WKWebView limitations. The WKWebView APIs are extremely shallow, not giving us enough room to customize. But a lot of requests that developers have can be solved if access to networking/request loading is available.

(We are building a browser, Firefox for iOS, but many of our wishes are pretty common and have valid use cases in 'normal' WKWebView usage too)
Comment 10 Sam Weinig 2016-05-23 10:52:47 PDT
(In reply to comment #9)
> (In reply to comment #5)
> > (In reply to comment #4)
> > > Are there any plans on implementing this functionality? I'd really love to
> > > use WKWebView, but this limitation forces me to keep using the UIWebView.
> > 
> > We are investigating it, but it would really help us to understand more
> > about why you were using this functionality. What type of loads did you need
> > to intercept?
> 
> Some things we would like to do with WKWebView that are currently impossible
> due to no ability to intercept and modify loading behaviour:

Thanks! A few additional questions:

> 
> * HTTPS Everywhere - Detect HTTP loads of resources in a whitelist, modify
> them to become HTTPS loads
> * Content blocking - Block resources, both top level and resource loads

Diving into these, are you doing dynamic analysis of of loads, or would a static matching / modification engine suite these needs?  (Similar to the Content Blocker feature in WebKit, though not exposed as API, but with the ability to modify requests in addition to blocking).

> * Better control over headers - Ability to look and modify headers, both
> incoming and outgoing

What are the use cases you have for these? Which headers do you need to you want to modify and why?

> * Implement custom content caching and content preloading - Need the ability
> to take over a resource load and provide our own response

What is the use case for this?

> * Better control of cookies. Currently there is no way to control cookie
> policy in WKWebView, this is trivial to imlement if we can control network
> requests

Which aspects of the cookie policy do you want to control? What are the use cases for it?
Comment 11 Sam Weinig 2016-05-23 10:58:33 PDT
(In reply to comment #6)
> In my particular case, this interception is needed for 2 main purposes:
> 1. Implement SSL pinning for web view content (most important)
> 2. Provide encrypted on-device assets, and be able to decrypt them before
> pass them to the web view for rendering.

Is this for resources with the http/https scheme? Or a different scheme?
Comment 12 Sam Weinig 2016-05-23 11:00:29 PDT
(In reply to comment #8)
> (In reply to comment #5)
> > (In reply to comment #4)
> > > Are there any plans on implementing this functionality? I'd really love to
> > > use WKWebView, but this limitation forces me to keep using the UIWebView.
> > 
> > We are investigating it, but it would really help us to understand more
> > about why you were using this functionality. What type of loads did you need
> > to intercept?
> 
> Glad to see that someone at Apple is picking this up. We even spent a TSI on
> asking this a long time ago. The anwser was basically "not considered a bug"
> so we shrugged and keep dragging the UIWebView cart.
> 
> We have built a web browser which allows for configurable intervention in
> the complete webview traffic, i.e. not just frames but all resources. Even
> XHR. The possible interventions are numerous. Cancel the request altogether,
> redirect, fake a response (e.g. a locally made image), tinker with outgoing
> or even incoming HTTP headers. It all works quite nicely on UIWebView, but
> it feels being left behind indeed.

To be clear, the functionality in UIWebView does not actually allow for intervention in all traffic.  Notably, most traffic for video and audio assets does not get captured as it happens in mediaserverd, not the host process.
Comment 13 Arthur 2016-05-23 16:03:21 PDT
> * Implement custom content caching and content preloading - Need the ability
> to take over a resource load and provide our own response

> What is the use case for this?

Upgrade Progressive Web App to Cordova app, with caching implemented on native level as plugin. i.e.website isn't packaged with app, but served from server and cached on device, much like ServiceWorker does.
Comment 14 Ignacio Calderon 2016-05-24 00:23:29 PDT
(In reply to comment #11)
> (In reply to comment #6)
> > In my particular case, this interception is needed for 2 main purposes:
> > 1. Implement SSL pinning for web view content (most important)
> > 2. Provide encrypted on-device assets, and be able to decrypt them before
> > pass them to the web view for rendering.
> 
> Is this for resources with the http/https scheme? Or a different scheme?

For (1), https resources.
For (2), main case file:// and optionally http*://. The assets could be on the app bundle encrypted to avoid tampering or reverse engineering, and accessed via file:// schemes. Or http/https equivalent to an end-to-end encryption, to dynamically update the content.
Comment 15 Daniel 2016-05-24 04:31:16 PDT
Hello guys,

Following up the discussion:
https://lists.webkit.org/pipermail/webkit-dev/2016-May/thread.html#28241
I'd like to share some use cases I have for this feature.

Opera on iOS implements a custom HTTP(S) protocol to do:
1. Data savings (see http://www.opera.com/turbo ). This greatly improves connectivity under crappy network conditions for millions of users. It's especially important for people in countries which can only dream about 4G.
2. Peer-to-peer inobtrusive security. For that we collect bits of site security information that is only available via low-level network APIs.
3. Presenting sites as icons (and grouping multiple pages into the same icons). For that we hook into the HTML data stream to parse meta data ASAP. In addition we intercept and react on HTTP redirects. This is a part of the http://operacoast.com app identity.
4. Progress loading reporting, automatic retries on bad networks. For that we do traffic QoS monitoring.
5. Fast going back and offline content. That is controlled partially by a custom cache, and partially in NSURLProtocol.
6. Ad-blocking.

Almost all of what Stefan Arentz says and other mentioned applies to us as well, as the need for: untrusted resources blocking, custom cache, content preloading, certificate pinning, HSTS.

With that said, it's so important feature to have for my team that
we are staying with WebKit1 for now.

See also:
https://bugs.webkit.org/show_bug.cgi?id=137299
https://bugs.webkit.org/show_bug.cgi?id=137302
https://bugs.webkit.org/show_bug.cgi?id=138131
Comment 16 Eugene But 2016-05-31 14:23:24 PDT
Early this year Chrome for iOS migrated from UIWebView to WKWebView. WKWebView gave us a huge improvement in stability, but inability to use custom network protocols made impossible to support the following features:

Privacy
 - Block 3rd party cookies
 - Do Not Track
 - Non-ephemeral Incognito browsing (so users are not logged out if they background the app and the app is killed in the background.)

Security
 - HSTS preloads
 - Public key pinning
 - Safe browsing
 - SHA-1 deprecation warning

Other features
 - Data Saver
 - Downloads if authentication is required (no access to cookies)
 - Multiple profiles support
 - QUIC network protocol
 - WebP image format
Comment 17 Brady Eidson 2016-05-31 15:17:57 PDT
(In reply to comment #16)
> Early this year Chrome for iOS migrated from UIWebView to WKWebView.
> WKWebView gave us a huge improvement in stability, but inability to use
> custom network protocols made impossible to support the following features:
> 
> Privacy
>  - Block 3rd party cookies
>  - Do Not Track
>  - Non-ephemeral Incognito browsing (so users are not logged out if they
> background the app and the app is killed in the background.)
> 
> Security
>  - HSTS preloads
>  - Public key pinning
>  - Safe browsing
>  - SHA-1 deprecation warning
> 
> Other features
>  - Data Saver
>  - Downloads if authentication is required (no access to cookies)
>  - Multiple profiles support
>  - QUIC network protocol
>  - WebP image format

This list is full of interesting API requests, but only a few are related to this bug.

I'd urge you to make sure there are independent bug reports for each.
Comment 18 Daniel 2016-06-01 01:31:46 PDT
(In reply to comment #17)
> (In reply to comment #16)
> > Early this year Chrome for iOS migrated from UIWebView to WKWebView.
> > WKWebView gave us a huge improvement in stability, but inability to use
> > custom network protocols made impossible to support the following features:
> > 
> > Privacy
> >  - Block 3rd party cookies
> >  - Do Not Track
> >  - Non-ephemeral Incognito browsing (so users are not logged out if they
> > background the app and the app is killed in the background.)
> > 
> > Security
> >  - HSTS preloads
> >  - Public key pinning
> >  - Safe browsing
> >  - SHA-1 deprecation warning
> > 
> > Other features
> >  - Data Saver
> >  - Downloads if authentication is required (no access to cookies)
> >  - Multiple profiles support
> >  - QUIC network protocol
> >  - WebP image format
> 
> This list is full of interesting API requests, but only a few are related to
> this bug.
> 
> I'd urge you to make sure there are independent bug reports for each.

All of the things here look very familiar to me. It looks related to NSURLProtocol support in a way that it enables all of those features. If NSURLProtocol support was introduced the developers could bring back all that code to implement the listed features.

One could possibly create separate bugs for each of the use cases that were listed, and solve them with separate methods, but I think that the call from Sam was to list all possible use cases. This points out how the lack of NSURLProtocol support affected the apps that migrated (or plan to migrate) to WKWebView, and shows which use cases have to be dropped because they were based on NSURLProtocol-s.
Comment 19 Brady Eidson 2016-06-01 09:23:45 PDT
(In reply to comment #18)
> (In reply to comment #17)
> > (In reply to comment #16)
> > > Early this year Chrome for iOS migrated from UIWebView to WKWebView.
> > > WKWebView gave us a huge improvement in stability, but inability to use
> > > custom network protocols made impossible to support the following features:
> > > 
> > > Privacy
> > >  - Block 3rd party cookies
> > >  - Do Not Track
> > >  - Non-ephemeral Incognito browsing (so users are not logged out if they
> > > background the app and the app is killed in the background.)
> > > 
> > > Security
> > >  - HSTS preloads
> > >  - Public key pinning
> > >  - Safe browsing
> > >  - SHA-1 deprecation warning
> > > 
> > > Other features
> > >  - Data Saver
> > >  - Downloads if authentication is required (no access to cookies)
> > >  - Multiple profiles support
> > >  - QUIC network protocol
> > >  - WebP image format
> > 
> > This list is full of interesting API requests, but only a few are related to
> > this bug.
> > 
> > I'd urge you to make sure there are independent bug reports for each.
> 
> All of the things here look very familiar to me. It looks related to
> NSURLProtocol support in a way that it enables all of those features. If
> NSURLProtocol support was introduced the developers could bring back all
> that code to implement the listed features.
> 
> One could possibly create separate bugs for each of the use cases that were
> listed, and solve them with separate methods, but I think that the call from
> Sam was to list all possible use cases. This points out how the lack of
> NSURLProtocol support affected the apps that migrated (or plan to migrate)
> to WKWebView, and shows which use cases have to be dropped because they were
> based on NSURLProtocol-s.

I'm not saying "stop listing your use cases here and please file a new bug on everything".

I just don't want this bug to start tracking "Here is every single gripe I have with the WKWebView API", which would be unproductive.

There's things in this list for which SPI already exists (e.g. 3rd party cookie blocking), and things in this list for which "Custom NSURLProtocol is the solution!" is not apparent to everyone.

WebP images for example: It's not at all apparent (to me, at least) why implementing a custom image format requires custom network loading. Maybe I'm missing something obvious?
Comment 20 Eugene But 2016-06-01 12:52:22 PDT
We already filed multiple bugs against specific features, which I can list here:

Block 3rd party cookies:
https://bugs.webkit.org/show_bug.cgi?id=140205

Do Not Track:
https://bugs.webkit.org/show_bug.cgi?id=140571

Non-ephemeral Incognito browsing:
https://bugs.webkit.org/show_bug.cgi?id=140201

Downloads if authentication is required:
https://bugs.webkit.org/show_bug.cgi?id=140191

Multiple profiles support:
https://bugs.webkit.org/show_bug.cgi?id=140201


We filed various radars against CFNetwork and Security frameworks to cover missing security features. However the fully working solution would be to allow using custom network protocols.


Concerning WebP support, Chrome did the following things:
1.) Appended WebP to Accept request header, so server could respond with WebP images
2.) Decoded responses with WebP mime types and replaced them with images supported by UIWebView
Neither request part, not response is possible with WKWebView.
Comment 21 Stefan Arentz 2016-06-01 13:20:01 PDT
(In reply to comment #20)

I also want to clarify something with regards to 'please file more detailed bugs' and the following is actually a great example:

> Concerning WebP support, Chrome did the following things:
> 1.) Appended WebP to Accept request header, so server could respond with
> WebP images
> 2.) Decoded responses with WebP mime types and replaced them with images
> supported by UIWebView
> Neither request part, not response is possible with WKWebView.

Since WKWebView has been introduced in iOS 8, almost 2 years ago, its API has moved forward / opened up *very very little*.

This despite dozens of feature requests being filed. Mozilla has filed a good number. So has every other browser vendor and many individual developers. Lots is still missing. From basics (cookie policy configuration or even setting custom headers) to more complicated things like access to networking. Which is what this bug is about.

Almost none of these filed feature requests have been implemented or even given attention on this bugtracker. This is not a surprise. WebKit on iOS is a complicated component and I think we all fully understand how Apple is a) resource constrained and b) cautious of introducing APIs that it needs to support into the very long term future.

The problem however is that after 2 years WKWebView is still very limited. So limited that people are talking about moving back to UIWebView. Mozilla included.

So back to the WebP example from above. I don't think that filing narrow and detailed requests like that is going to make a difference. The past years have proven that.

This is why everyone is asking to open up WKWebView more and specifically the networking part of it. These are *generic* changes we are asking for. Open ended. It is not specific on purpose, so that we, developers, can just do what we need to do to build interesting and competitive features. Without having to ask Apple to spend engineering time on a long list of very specific features.

Networking is the thread that connects most of the things that we want to do. With access to networking we can implement *so many* things that are now missing in WKWebView.

And that is why WebP is actually a great example. A technically complicated feature for Apple to implement. (CoreImage changes, policitical, etc.) But Google can just do it if they could set headers and intercept loads.

Networking is that Generic API that would open up WKWebView and make it 10x more usable.

I'll keep filing specific bugs for thigns that we want. Pretty much everything mentioned in this thread already has bugs. But also please consider opening up the networking for the above reasons.
Comment 22 Daniel 2016-06-03 00:31:54 PDT
> 
> Concerning WebP support, Chrome did the following things:
> 1.) Appended WebP to Accept request header, so server could respond with
> WebP images
> 2.) Decoded responses with WebP mime types and replaced them with images
> supported by UIWebView
> Neither request part, not response is possible with WKWebView.
>

Opera on iOS uses the same strategy to support WebP, which is a part of our data savings feature. In the savings mode all the images are transcoded through this format.
Comment 23 Erwin van Dijk 2016-06-23 05:06:28 PDT
Intercepting web requests for me is no longer necessary. The reason I used this was for caching large binary files. This can be achieved by using IndexedDB as well. (https://developer.mozilla.org/nl/docs/IndexedDB ) The reason that I post this update is that it might help other people who only need the interception for caching files.

Of course this does not solve 'behind the scenes encryption', compression, etc. Therefore the issue can't be closed.
Comment 24 drew 2016-07-16 20:20:23 PDT
We use NSURLProtocol to actually use a non-HTTP(S) wire protocol.  It's more efficient than the traditional stuff and provides a real UX improvement for various classes of captive native/web applications.

A lot of the usecases in this thread could (in theory) be resolved by new API surface–content blocking, cookies, etc could all be solved by new API.

However, for folks who actually need to intercept the requests themselves, the existing API was very good, and it's a real shame to see it go.
Comment 25 Garvan Keeley 2016-12-01 14:21:00 PST
NSURLProtocol interception of WKWebView http/https is theoretically possible:

Class cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
[(id)cls performSelector:sel withObject:@"http"];
[(id)cls performSelector:sel withObject:@"https"];

Followed by an NSURLProtocol registration will intercept all calls.

Credit to this github user who also has a demo project https://github.com/WildDylan/WKWebViewWithURLProtocol

Given that this already works, perhaps Apple can consider exposing it officially.
Comment 26 Arthur 2016-12-05 11:34:08 PST
to #c25

Or at least not block that thing..
Comment 27 Garvan Keeley 2016-12-06 09:21:39 PST
There is a major bug in the comment #25 approach, having a registered NSURLProtocol causes post bodies to be stripped.

Returning false always from NSURLProtocol `canInitWithRequest(request: NSURLRequest) -> Bool` results in an empty post body. 

The stripped post body in the NSURLRequest sent to NSURLProtocol is not surprising given the justification in https://bugs.webkit.org/show_bug.cgi?id=145410, that IPC serialization of large post data is a performance hit. What is surprising is that returning 'false' from `canInitWithRequest` does not workaround this.
Comment 28 Pavel Zdenek 2016-12-06 12:19:03 PST
(In reply to comment #27)
> There is a major bug in the comment #25 approach, having a registered
> NSURLProtocol causes post bodies to be stripped.

I can imagine it being not a bug, but actually an intentional logic along the original behavior of not sending POST body. Where in both cases, the intent correctness can be discussed, but not treated as a bugfix. I have spent some time looking into WK2 sources, hoping to find an approximate place of such "bug". I got a gut feeling instead: when registerSchemeForCustomProtocol is invoked, things appear to get flipped to a "custom protocol" primarily on WK side and the IPC is just a subsystem. So a stripped POST request is made for that custom protocol, and when IPC pipe spits out "not interested", WK side simply goes on with the request already made. Take this with a shovel of salt, i'm a github skimmer, not WK contributor.
Comment 29 Ian Ragsdale 2017-02-01 09:46:42 PST
Adding a plus one for this bug. My day job is working for VMware/AirWatch, and we have a custom browser that exists to provide access to corporate sites behind a firewall. We use a custom NSURLProtocol to allow tunneling requests to the back end, and we are completely unable to use WKWebView for our purposes.

It looks like some of the code we need is open source. If we invested developer time in extending the WKWebView to allow this, would Apple even be willing to ship it?
Comment 30 Steve Sauder 2017-03-26 02:45:15 PDT
I have an additional use case that might be useful - i'm writing a WKWebKit-based application - not based on Cordoba, but built myself, that is intended to be used in a situation where it may be required to function without network connectivity for a period of time - and it must have a fairly large set of images cached on the device (read: larger than localStorage or other storage methods would permit) in order to maintain it's functionality while temporarily offline.  The only useful way I've found to be able to do that is to use NSURLProtocol to manage caching of the imagery at the app level (using CoreData).  There doesn't appear to be any other comprehensive way to intercept the network requests and provide our own caching mechanism without using NSURLProtocol.
Comment 31 pyanfield 2017-06-08 19:41:57 PDT
I have a use case that when loading a webpage with many images, I want to know which image is loaded failed and then use a placeholder to replace it. But for now, I can't get the information.