I want to configure one aspect of my networking configuration (the QUIC keepalive interval). This only seems to be configurable via Network.framework’s nw_quic_set_keepalive_interval
. Is there any way to apply this to a URLSession? Or do I need to implement the whole connection management myself using Network.framework?
URLSession QUIC configuration
URLSession controls the QUIC keep alive interval and adjusts it automatically.
- When an outstanding HTTP/3 request is sent on the QUIC connection, the interval is set to 1s.
- While we are receiving the response body, the interval is set to 20s to prevent NAT timeouts.
- When the connection is idle, keepalives are disabled.
Would you share your use case for configuring the interval?
We need to configure the interval due to connection errors for iOS clients behind a firewall when connecting to Cloudflare. After a lengthy investigation, we discovered the following combination results in hangs/stalls for iOS apps and even Safari:
-
the iOS default QUIC client max_idle_timeout of 0
-
middlebox NAT router/firewall's that perform NAT rebinding
-
Cloudflare’s lack of response to NAT rebinding
In more detail, this is because:
- iOS (and Safari OSX) uses a default max_idle_timeout of 0, doesn’t send keepalives, therefore QUIC connections will use the server supplied idle timeout value
- some middlebox devices perform NAT rebinding where the source port or IP may change along with a short UDP session timeout
- Cloudflare sets a max_idle_timeout of 180sec and completely ignores connection migrations
- iOS users will have hangs of at least 6sec if they perform a request between 30 and 180sec from a previous request
- this seems to affect the iOS stack altogether, we were able to reproduce the stalls with Safari and our other apps as well
The QUIC RFC 9000 recommends a keep-alive of 30sec and specifically calls out middleboxes with 30sec udp timeouts
We have confirmed using a small Go app that specifying a 30s max_idle_timeout resolves the issue.
We hope to configure URLSession to also have a 30s timeout, but it doesn’t seem to be possible, and we would need to re-implement the connection management stack using Network.framework to do so.
We've opened FB16097749 and can provide more detail if needed.
Thanks for filing a feedback. We will discuss how to move forward.
Is there any new progress update on this?
I seem to have encountered this problem as well. How can we avoid this issue?