App cannot upgrade to http/3 when running on iOS 16

My team are now testing http/3 support in our app and backend. It looks strange to me how the behaviour is different between iOS 15.7.3 and 16.x (now 16.6).

It is a default URLSession config and just a regular URLRequest, with no assumesHTTP3Capable flag set. I observe the protocol in use with both collecting URLSessionTaskMetrics and the Network instrument.

On 15.7.3 the 1st request is done using h2, the network subsystem caches the Alt-Svc header (per host I suppose). Several seconds pass and then when I issue the same 2nd request a new connection is established and the protocol is upgraded to h3. This is all fine and as described in the documentation or wwdc videos.

However, when I take the same steps with a device running e.g. iOS 16.5.1 I see that the protocol is never upgraded to h3. Neither a longer timeout nor an app relaunch make any difference.

Interestingly, on my Ventura desktop the same url is also handled differently: h3 by Chrome but always h2 by Safari. In the mobile Safari on iOS 16, I'm also always shown HTTP/2 on e.g. the cloudflare status page for QUIC.

What can be the reason for such behaviour on iOS 16 and Apple platforms in general?

PS. I've tried running the app with CFNETWORK_DIAGNOSTICS but found no useful log messages to clarify problems with QUIC. Is there still a way to better diagnose such a problem?

However, when I take the same steps with a device running e.g. iOS 16.5.1 I see that the protocol is never upgraded to h3. Neither a longer timeout nor an app relaunch make any difference.

This all depends upon how CFNetwork (the library the runs URLSession) classifies the hostname being used. First I will say that you will greatly improve your chances of having H3 used here if assumesHTTP3Capable is active.

Next, if you have some logs reproducing this case in iOS 16.5.1 and can post them here that would be great. Please include all CFNetwork logs that are included from the time resume is first called on the task.

We have been testing our iOS app and QUIC for a while now, and I'd like to share some more observations.

In short, Even though we do get the Alt-Scv header from our backend, the app might utilize HTTP/3 on one day and completely ignore it on other days.

A simple test client for MacOS confirmed the behaviour is the same not only with our backend but also e.g. quic.nginx.org and cloudflare-quic.com. Our metrics say that only about 30% of our clients get QUIC.

While researching here and there on the web we came to the /Library/Preferences/com.apple.networkd.plist file, and its enable_quic parameter was something that surely caught our eyes. With this we surveyed some of our colleagues. It turned out that when they had this flag enabled (set to 1) - h3 was working both in Safari and the test client, otherwise (set to 0) the best they could get was HTTP/2 (roughly half of them).

To me this feels not aligned well with the description of the QUIC discovery from WWDC. Would you please explain the rationale behind the enabling logic described above?

This all depends upon how CFNetwork (the library the runs URLSession) classifies the hostname being used.

Is there anything (besides assumesHTTP3Capable) we can do to help CFNetwork better classify the hostname for QUIC?

I have encountered a related problem (see https://developer.apple.com/forums/thread/758255). I am wondering if you saw QUIC always enabled if assumesHTTP3Capable is true? (regardless enable_quic parameter in /Library/Preferences/com.apple.networkd.plist)

In our case, we always use assumesHTTP3Capable to ensure a request is processed by QUIC. But we hit a blocker when a phone running iOS 16.0.2 always uses TCP (h2 I suppose) for some reason. (The backend only supports H3 / QUIC).

App cannot upgrade to http/3 when running on iOS 16
 
 
Q