Create network connections to send and receive data using the QUIC protocol.

Posts under QUIC tag

22 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

QUIC Network framework interoperability
We would like to understand/double check if it is possible to use QUIC in Swift via Network framework as the client along with some other QUIC solution on the server (ex. s2n-quic, quiche, msquic, etc..) which won't be a macOS server. If that interoperability is indeed possible, the NWConnectionGroup won't be an approach we could use IMO, since probably we will need to develop that from scratch on both sides. Thanks in advance.
1
0
40
3h
assumesHTTP3Capable not working only on some iPhones
Hi, We are using HTTP3 only and hence using assumesHTTP3Capable for every request. It worked so far but now encountered one iPhone that never honor this flag and always tries to create a connection using TCP: [tcp] tcp_input [C1:3] flags=[R.] seq=0, ack=2023568485, win=0 state=SYN_SENT rcv_nxt=0, snd_una=2023568484 The request is created like this: let url = URL(string: urlString)! var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0) request.assumesHTTP3Capable = true return try await urlSession.data(for: request) iOS: 16 XCode: 15.3 In what cases iOS CFNetwork would not honor "assumesHTTP3Capable" ? (or how can I find out why?)
4
0
197
1w
How to find out network connection error details
Hi, When running my iOS app in Xcode, I got the following message in the console multiple times: [connection] nw_read_request_report [C1] Receive failed with error "Operation timed out" It seems not critical as my app still works, but how can I find out more details of the connection that printed this message? For example, the network request the caused this, or the URL? Xcode: 15.3 iOS 17 SwiftUI app
5
0
275
4w
QUIC Connection Group Server Sending Pace
We have an implementation in which we use QUIC via a connection group, server are client are on Swift using the Network framework. Our use case is, the server should send data buffers to the client as fast and as much as possible, now the pace to call the send method from the server should be carefully done, because if we send too much data of course the client is not gonna be able to receive it. The question would be, is there a way to query the congestion window so we know on the server side, how much data we should be able to send at some point? Asking because we are not getting all the data we are sending from the server on our client side... We are using these settings: let options = NWProtocolQUIC.Options(alpn: ["h3"]) options.direction = .bidirectional // options.idleTimeout = 86_400_000 options.maxUDPPayloadSize = Int.max options.initialMaxData = Int.max options.initialMaxStreamDataBidirectionalLocal = Int.max options.initialMaxStreamDataBidirectionalRemote = Int.max options.initialMaxStreamDataUnidirectional = Int.max options.initialMaxStreamsBidirectional = 400 options.initialMaxStreamsUnidirectional = 400 Questions: 1.- Can we get a little more detail in above options, specifically on their impact to the actual connection? 2.- IsinitialMaxData the actual congestion window value 3.- Are we missing something or making incorrect assumptions? Thanks in advance.
10
0
386
2w
NWConnectionGroup w/QUIC Best Practices
Hello. Wanted to ask about the right way, or the intended way to leverage NWConnectionGroup for a QUIC based streaming solution. The use case is, we are making a request from the client in order to play a movie, and we want to send as much video frames as possible (and as fast as possible) from the streaming server, which also uses the Network framework. Our understanding is, NWConnectionGroup will open a QUIC tunnel between both parties so we can multiplex different streams to the client and we are already doing that. We see a throughput of approx. 20-35MB/s (client device is an iPad and server is an M2 macbook pro running a server app) and we would like to understand if we can improve these results way more. For example: 1.- Is it a good practice to create a second tunnel (NWConnectionGroup), or is not needed here?. We tried that, but the second one is also coming with id 0 on the metadata object, just as the first group we instantiated, not sure why this is the case. 2.- We are using a pool of several NWConnection (initialized with the group object) already instantiated, that way we send a video buffer in chunks as a stream on each connection. We use one connection for a buffer and when we need to send another buffer we use a different NWConnection pulled from the pool. We maybe just want a confirmation/validation of what we are doing, or to see if we are missing something on our implementation... Thanks in advance.
2
0
256
Jun ’24
Crash in Network framework nw_queue_context_async + 76 (queue.m:87)
Hi, We recently released a version of our app where we use 'NWParameters.PrivacyContext'. On iOS 17.4 and iOS 17.4.1 the app crashes with a following crash: Distributor ID: com.apple.AppStore Hardware Model: iPhone12,1 Process: <app> Path: <path to app> Identifier: <bundle id> Version: <version> AppStoreTools: 15E204 AppVariant: 1:iPhone12,1:15 Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Coalition: <our bundle id> [4899] Date/Time: 2024-04-29 01:50:13.4113 +0300 Launch Time: 2024-04-29 01:13:47.6252 +0300 OS Version: iPhone OS 17.4.1 (21E236) Release Type: User Baseband Version: 5.00.00 Report Version: 104 Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000054 Exception Codes: 0x0000000000000001, 0x0000000000000054 VM Region Info: 0x54 is not in any region. Bytes before following region: 4334124972 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 102558000-1063e4000 [ 62.5M] r-x/r-x SM=COW <path to app> Termination Reason: SIGNAL 11 Segmentation fault: 11 Terminating Process: exc handler [24308] Triggered by Thread: 18 Kernel Triage: VM - (arg = 0x3) mach_vm_allocate_kernel failed within call to vm_map_enter VM - (arg = 0x3) mach_vm_allocate_kernel failed within call to vm_map_enter VM - (arg = 0x3) mach_vm_allocate_kernel failed within call to vm_map_enter VM - (arg = 0x3) mach_vm_allocate_kernel failed within call to vm_map_enter VM - (arg = 0x3) mach_vm_allocate_kernel failed within call to vm_map_enter Thread 18 name: Thread 18 Crashed: 0 libdispatch.dylib 0x00000001a573b7d8 dispatch_async + 192 (queue.c:940) 1 Network 0x000000019ddbdb38 nw_queue_context_async + 76 (queue.m:87) 2 Network 0x000000019e512748 invocation function for block in nw_socket_init_socket_event_source(nw_socket*, unsigned int) + 1488 (protocol_socket.cpp:4351) 3 libdispatch.dylib 0x00000001a5736dd4 _dispatch_client_callout + 20 (object.m:576) 4 libdispatch.dylib 0x00000001a573a2d8 _dispatch_continuation_pop + 600 (queue.c:321) 5 libdispatch.dylib 0x00000001a574e1c8 _dispatch_source_latch_and_call + 420 (source.c:596) 6 libdispatch.dylib 0x00000001a574cd8c _dispatch_source_invoke + 832 (source.c:961) 7 libdispatch.dylib 0x00000001a5740284 _dispatch_workloop_invoke + 1756 (queue.c:4570) 8 libdispatch.dylib 0x00000001a5749cb4 _dispatch_root_queue_drain_deferred_wlh + 288 (queue.c:6998) 9 libdispatch.dylib 0x00000001a5749528 _dispatch_workloop_worker_thread + 404 (queue.c:6592) 10 libsystem_pthread.dylib 0x00000001f981cf20 _pthread_wqthread + 288 (pthread.c:2665) 11 libsystem_pthread.dylib 0x00000001f981cfc0 start_wqthread + 8 (:-1) Thread 18 crashed with ARM Thread State (64-bit): x0: 0x0000000301a922e0 x1: 0x000000032471a720 x2: 0x0000000000000000 x3: 0x00000003015e2300 x4: 0x0000000000000003 x5: 0x00000000000022e0 x6: 0x0000000172462ef0 x7: 0x000000000000008b x8: 0x00000000000008ff x9: 0x0000000000000000 x10: 0x0000000000010000 x11: 0x0000000000000020 x12: 0x00000003016f3854 x13: 0x00000000001ff800 x14: 0x00000000000007fb x15: 0x0000000089800118 x16: 0x00000001a573511c x17: 0x000000019e514740 x18: 0x0000000000000000 x19: 0x0000000000000000 x20: 0x0000000308eae8c0 x21: 0x000000032471a730 x22: 0x000000032471b0e0 x23: 0x000000000000e023 x24: 0x0000000172463085 x25: 0x000000002b1d034c x26: 0x0000000000000800 x27: 0x0000000000000000 x28: 0x0000000000000000 fp: 0x000000032471a6b0 lr: 0xad5ba301a573b750 sp: 0x000000032471a690 pc: 0x00000001a573b7d8 cpsr: 0x80000000 esr: 0x92000006 (Data Abort) byte read Translation fault What could be the reason for it?
2
0
363
Apr ’24
NWConnectionGroup no way to send/receive on QUIC stream with identifier `0`
Hello! I'm playing around with QUIC and Swift and using the Network framework. So far, the process has been really straightforward, but I noticed that I can't seem to get a handle on the stream with identifier 0. If I use NWConnection directly, I only have access to the first stream, which has the stream ID 0. This not what I want since I wanna use multiple streams. Following the documentation, I started using NWMultiplexGroup and starting a NWConnectionGroup with it. Everything works fine and I can get all streams that my backend service opens using NWMultiplexGroup's newConnectionHandler property. However, whenever backend sends a message on stream_id 0, none of my connections receive it. Looking around with connection.metadata(definition: NWProtocolQUIC.definition) as? NWProtocolQUIC.Metadata for each connection, I see that all streams are accounted for except stream 0. Then, using the NWConnectionGroup variant of the above connectionGroup.metadata(definition: NWProtocolQUIC.definition) as? NWProtocolQUIC.Metadata I see that the connection group itself has Stream ID 0. However, calling setReceiveHandler does nothing (it's never called, even when backend is sending messages) and when I attempt to send a message using NWConnectionGroup's -send method, a new stream is opened (instead of it being sent on stream ID 0). How can one get a handle on NWConnection for stream ID 0?
1
0
444
Apr ’24
Troubleshooting Peer-to-Peer Connection Failure between iOS Apps Using NWListener, NWConnection, and STUN
I am currently developing two iOS applications that require peer-to-peer connectivity. To facilitate this, I've implemented NWListener and NWConnection in both apps for network communication. To determine each device's public IP address and port—necessary for establishing a connection over the internet through my mobile operator's Carrier-Grade NAT (CGNAT)—I'm using a STUN server. Despite successfully retrieving the external IP addresses and ports for both devices, I am unable to establish a peer-to-peer connection between them. My current setup involves initiating a connection using the public addresses and ports discovered through the STUN server response. However, all attempts to connect the devices directly have been unsuccessful. I am seeking guidance on whether there are additional considerations or specific configurations needed when using NWListener, NWConnection, and a STUN server to establish a direct connection between devices in a CGNAT environment. Is there a particular step or network configuration I might be missing to successfully connect both iOS devices to each other using their external network details?
1
0
477
Mar ’24
QUIC connection error
Hi, we are currently implementing below method for a quick POC in iOS (Xcode 15.3/macOS Sonoma 14.0): func startQUICConnection() async { // Set the initial stream to bidirectional. options.direction = .bidirectional self.mainConn?.stateUpdateHandler = { [weak self] state in print("Main Connection State: \(state)") switch state { case .ready: print("Ready...") default: break } } // Don't forget to start the connection. self.mainConn?.start(queue: self.queue) } This is what we have in the initializer of the class: parameters = NWParameters(quic: options) mainConn = NWConnection(to: endpoint, using: parameters) These are the class's properties: let endpoint = NWEndpoint.hostPort(host: "0.0.0.0", port: .init(integerLiteral: 6667)) let options = NWProtocolQUIC.Options(alpn: ["echo"]) let queue = DispatchQueue(label: "quic", qos: .userInteractive) var mainConn: NWConnection? = nil let parameters: NWParameters! As per the logs, we never get to the .ready state for the NWConnection. Logs: nw_path_evaluator_create_flow_inner failed NECP_CLIENT_ACTION_ADD_FLOW (null) evaluator parameters: quic, attach protocol listener, attribution: developer, context: Default Network Context (private), proc: 022B7C28-0271-3628-8E5E-26B590B50E5B nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 8FEBF750-979D-437F-B4A8-FB71F4C5A882 [22: Invalid argument] nw_endpoint_flow_setup_channel [C2 0.0.0.0:6667 in_progress channel-flow (satisfied (Path is satisfied), interface: en0[802.11], ipv4, ipv6, dns, uses wifi)] failed to request add nexus flow Main Connection State: preparing Main Connection State: waiting(POSIXErrorCode(rawValue: 22): Invalid argument) We're running a local server using proxygen on port 6667. It connects with the proxygen client though... Have tried several thing but results are the same.
1
0
996
Mar ’24
QUIC receive get NWProtocolMetadata for all involved protocols
I working on a QUIC Client/Server and would like to inspect all underlying protocols via NWConnection.ContextContent in the receive method. receiveMessage(completion: {(receivedContent, context, isComplete, receivedError) .receive(minimumIncompleteLength: 1, maximumLength: 65535) { (receivedContent, context, isComplete, receivedError) As far as I understand is that the parameter .protocolMetadata in ContextContent should provide a list of all involved protocols. I expect an array of 3 NWProtocolMetadata like [NWProtocolIP.Metadata, NWProtocolUDP.Metadata, NWProtocolQUIC.Metadata] but I only get [NWProtocolQUIC.Metadata]. I already managed to get [NWProtocolIP.Metadata, NWProtocolUDP.Metadata] for a UDP connection but I can't get it to work for QUIC. Is it possible to get NWProtocolIP.Metadata, NWProtocolUDP.Metadata for a QUIC connection within the receive function? Regards Jan
0
0
437
Feb ’24
NWConnectionGroup stateUpdateHandler not called for failed states
I'm building a network client with Swift (using QUIC). I set everything up properly (I know this because I can successfully connect, send and receive streams). But I'm trying to catch connection errors. For example if I try to connect to a totally bogus IP address, I would like to display Connecting, then ConnectionFailed I do the following: create my NWMultiplexGroup descriptor set my appropriate NWParameters create my NWConnectionGroup set up my handlers (setReceiveHandler, newConnectionHandler) and my state update handler i call connection.start When I pass a valid address to a server that is listening for the connection, all is good - in my stateUpdateHandler I get the .ready state, but I don't get any intermediate states, and if I pass it a bogus IP address, I get absolutely no callbacks to my handler (I would have expected to get .waiting and/or .failed) I couldn't find any quic options that I'm not doing, and the apple documentation is not helpful Any suggestions as to what I might be missing?
0
0
504
Jan ’24
Weird behaviour with Network Framework and QUIC Multiplex Groups.
Hey all here is an example you can try out: https://github.com/paxsonsa/quic-swift-demo I am prototype a QUIC base application system with a client and server. My server is a simple test to experiment with QUIC and Network Framework but I am see some odd behaviour. Selecting Stream Direction for new streams In the example below, we are creating a new multiplexed QUIC connection and establish a new stream once the group connection is ready. In some cases, I want to be able to use a different stream kind (uni/bi). By specifying the options, I get an error in Xcode console like so: running.... group state: waiting(POSIXErrorCode(rawValue: 50): Network is down) group state: ready Connected using QUIC! nw_endpoint_flow_setup_cloned_protocols [C3 127.0.0.1:4567 in_progress socket-flow (satisfied (Path is satisfied), viable, interface: lo0)] could not find protocol to join in existing protocol stack nw_endpoint_flow_failed_with_error [C3 127.0.0.1:4567 in_progress socket-flow (satisfied (Path is satisfied), viable, interface: lo0)] failed to clone from flow, moving directly to failed state Main Connection State: failed(POSIXErrorCode(rawValue: 50): Network is down) quic_recovery_pto PTO fired after validation Here is my swift code: // // main.swift // QuicTool // // Created by Andrew Paxson on 2024-01-14. // import Foundation import Network /// Helper function to create a message frame. func createMessage(version: UInt8, messageType: UInt8, message: String) -> Data { let messageData = message.data(using: .utf8) ?? Data() let length = UInt32(messageData.count) var data = Data() data.append(version) data.append(messageType) // Convert length to 4 bytes and append (big-endian format) let bigEndianLength = length.bigEndian data.append(contentsOf: withUnsafeBytes(of: bigEndianLength) { Array($0) }) // Append 2 bytes of padding for 8-byte alignment data.append(Data(repeating: 0, count: 2)) // Add Message Data. data.append(messageData) return data } // Queue for QUIC things. let queue = DispatchQueue(label: "quic", qos: .userInteractive) // Create Inital Options for the tunnel. // This is using an insecure connection as this operation is meant to be local network. let endpoint = NWEndpoint.hostPort(host: "127.0.0.1", port: .init(integerLiteral: 4567)) let options = NWProtocolQUIC.Options(alpn: ["demo"]) // Set the initial stream to bidirectional. options.direction = .bidirectional sec_protocol_options_set_verify_block(options.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in sec_protocol_verify_complete(true) }, queue) let parameters = NWParameters(quic: options) // 1) Create a new multiplexed connection let descriptor = NWMultiplexGroup(to: endpoint) let group = NWConnectionGroup(with: descriptor, using: parameters) var mainConn: NWConnection? = nil // Here we are establishing a state handler for when the connection to the // the server is neogiated and "ready". Once its ready we want to establish a // stream using the group with the options set. // // This is the main location of the issue we are seeing where the stream is // established and the data is sent but never updated. group.stateUpdateHandler = { newState in print("group state: \(newState)") switch newState { // Once the tunnel is established, create a new stream with bidirectional parameters. case .ready: print("Connected using QUIC!") // 2) In normal application I may want to open different kinds of streams in providing // new options. Is there a better way to select the stream kind for subsequent streams? let options = NWProtocolQUIC.Options(alpn: ["demo"]) options.direction = .bidirectional // When providing unique options the stream will fail. Removeing the using argument works. mainConn = group.extract()! // force unwrap mainConn?.stateUpdateHandler = { state in print("Main Connection State: \(state)") switch state { case .ready: // Once the connection is ready, lets send some sweet data sauce. // // By establishing this new stream and sending data, on the server this causes the inital // stream with no handle to be open. let version: UInt8 = 1 let messageType: UInt8 = 1 let message = "hello, I am from the multiplex group ready." let messageData = createMessage(version: version, messageType: messageType, message: message) mainConn?.send(content: messageData, isComplete: true, completion: .contentProcessed({ sendError in if let error = sendError { print("There was an error sending data: \(error)") } else { print("Data was sent successfully from Main Connection.") } })) default: break } } // Don't forget to start the connection. mainConn?.start(queue: queue) default: break } } // Receive new incoming streams initiated by the remote endpoint // this is not used for this example. group.newConnectionHandler = { conn in print("New Connection: \(conn)") // Set state update handler on incoming stream conn.stateUpdateHandler = { newState in print("newState: \(newState) for \(conn)") switch newState { case .ready: print("got a new stream!") default: break } } // Start the incoming stream conn.start(queue: queue) } // Start the group with callback queue group.start(queue: queue) print("running....") // We iterate trying to send data on the new stream we created after the // connection is established. while true { switch mainConn?.state { case .ready: // Once the connection is ready, lets send some sweet data sauce. let version: UInt8 = 1 let messageType: UInt8 = 1 let message = "hello, im from the main loop" let messageData = createMessage(version: version, messageType: messageType, message: message) print("Local Stream Send: \(messageData)") mainConn?.send(content: messageData, completion: .contentProcessed({ sendError in if let error = sendError { print("There was an error sending data: \(error)") } })) sleep(1) default: continue } }
8
0
982
Jan ’24
Does Facetime use QUIC ?
Hello, I noticed that Facetime attempts to use the QUIC protocol during a Facetime session with the following ports 3478 through 3497 (UDP). Can Facetime use the QUIC protocol for the following ports 3478 through 3497 (UDP) because on the internet I have not found anywhere the possibility that QUIC can be used other than ports 80 and 443?
1
0
699
Jan ’24
I want to use a QUIC stream with Swift's NWProtocolQUIC
I would like to use NWProtocolQUIC in Swift's Network.framework to prepare multiple QUIC Streams and send different data to the server for each. class QuicConnection { var acceptConnection: NWConnection? var openConnection: NWConnection? var acceptConnectionState: NWConnection.State? var openConnectionState: NWConnection.State? var receiveFromAcceptConnection: String? static func makeNWParameters() -> NWParameters { let options = NWProtocolQUIC.Options(alpn: ["echo"]) options.direction = .bidirectional let securityProtocolOptions: sec_protocol_options_t = options.securityProtocolOptions sec_protocol_options_set_verify_block(securityProtocolOptions, { (_: sec_protocol_metadata_t, _: sec_trust_t, complete: @escaping sec_protocol_verify_complete_t) in complete(true) }, DispatchQueue.main) return NWParameters(quic: options) } let group: NWConnectionGroup init() { print("init") let parameters = Self.makeNWParameters() let descriptor = NWMultiplexGroup(to: .hostPort(host: "192.168.0.20", port: 4242)) group = NWConnectionGroup(with: descriptor, using: parameters) //subscribe() group.stateUpdateHandler = { (state: NWConnectionGroup.State) in print("state: \(state)") switch state { case .ready: print("quic connected!") default: break } } group.newConnectionHandler = { [weak self] (connection: NWConnection) in print("new connection: \(connection)") self?.acceptConnection = connection self?.acceptConnection?.stateUpdateHandler = { [weak self] (state: NWConnection.State) in self?.acceptConnectionState = state } self?.subscribeAcceptConnection() self?.acceptConnection?.start(queue: DispatchQueue.main) } group.start(queue: DispatchQueue.main) } func createStream() { //guard let group else { return } let options = NWProtocolQUIC.Options() options.direction = .bidirectional let securityProtocolOptions: sec_protocol_options_t = options.securityProtocolOptions sec_protocol_options_set_verify_block(securityProtocolOptions, { (_: sec_protocol_metadata_t, _: sec_trust_t, complete: @escaping sec_protocol_verify_complete_t) in complete(true) // Insecure !!! }, DispatchQueue.main) openConnection = NWConnection(from: group) openConnectionState = openConnection?.state openConnection?.stateUpdateHandler = { [weak self] (state: NWConnection.State) in self?.openConnectionState = state print("state: \(state)") switch state { case .ready: print("stream connected!") DispatchQueue.main.asyncAfter(deadline: .now() + 2) { self?.send(message: "marker1") } default: break } } openConnection?.start(queue: DispatchQueue.main) } func send(message: String) { print("send start") let completion: NWConnection.SendCompletion = .contentProcessed { (error: Error?) in if let error = error { print("send error: \(error)") } else { print("send successful") } } openConnection?.send(content: message.data(using: .utf8)!, contentContext: .defaultMessage, isComplete: true, completion: completion) print("message: \(message)") } } When the app starts, it calls the init function of this QuicConnection class to create an instance and build the QUIC tunnel." quic connected" log appears, and when a specific action is performed in the app, the createStream function is called to put up a stream." stream connected" log is displayed, but when I then try to send data using the send function, the "send successful" message is displayed, but there is no output on the server side indicating that the message was received from the client. However, when I try to send data using the send function after that, I get a "send successful" message. I don't think there is a problem on the server side, because I can communicate well when only NWConnection is used without NQConnectionGroup. The server is using quic-go. I would like to borrow knowledge from those who have handled QUIC streams in Swift. Below are Apple's announcement and official documents that I referred to. I wrote the code referring to these, but even though I can connect the QUIC tunnels, I can not send data by setting up individual streams. https://developer.apple.com/videos/play/wwdc2021/10094/ https://developer.apple.com/documentation/network/nwprotocolquic
3
1
1.1k
Jan ’24
Background URLSession http/3 URLSessionUploadTask not started in real device
I'm trying to upload large files in a background URLSession to a server that accepts HTTP/3 requests. Per its design, the server uses a self-signed SSL certificate. In my testing, I can create a regular URLSession (foreground) with the following: var request = URLRequest(url: dst, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 6.0) request.assumesHTTP3Capable = true request.httpMethod = "POST" self.session.dataTask(with: request) { &lt;snip&gt; } And handles the SSL certificate challenge by implementing the following for the delegate: func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping @Sendable (URLSession.AuthChallengeDisposition, URLCredential?) -&gt; Void ) This approach works and I can communicate with the server. But when I try to apply the same for a background session for an upload task, the task seems to be stuck, i.e. never started by the system. More specifically, the background session upload task works with iOS simulator. But on a real device connected to macOS, the task never started, and none of the delegate methods are called. The http/3 server didn't receive any request either. The background session was created: let appBundleName = Bundle.main.bundleURL.lastPathComponent.lowercased().replacingOccurrences(of: " ", with: ".") let sessionIdentifier: String = "com.networking.\(appBundleName)" let config = URLSessionConfiguration.background(withIdentifier: sessionIdentifier) self.session = URLSession(configuration: config, delegate: self, delegateQueue: nil) later, an upload task was created : var request = URLRequest(url: dst, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 6.0) request.assumesHTTP3Capable = true request.httpMethod = "POST" let backgroundTask = self.session.uploadTask(with: request, fromFile: src) backgroundTask.resume() My question(s): why is the background session upload task stuck? Is there a way to find out where it is stuck? Does a background URLSession support app's delegate handling SSL certificate challenge? The test env: Xcode 14.2 macOS 12.6.7 iOS 15.7.7 Thanks!
4
0
1k
Aug ’23
iOS17.0 quic_recovery_declare_packets_lost Crash
A large number of crashes occurred only on iOS 17.0 online from July 27, which is consistent with the release time of the 17.0 beta4 version. I suspect something is wrong with 17.0 beta4. Does anyone have the same problem and is there a solution? Thanks a lot Crashed: com.apple.CFNetwork.Connection 0 libquic.dylib 0x21120 quic_recovery_declare_packets_lost + 928 1 libquic.dylib 0x20788 quic_recovery_find_lost_packet_inner + 1272 2 libquic.dylib 0x1e564 quic_recovery_find_lost_packets + 352 3 libquic.dylib 0x11b74 quic_recovery_received_ack + 1180 4 libquic.dylib 0x52634 quic_frame_process_ACK + 368 5 libquic.dylib 0xb3bc8 quic_conn_process_frame + 964 6 libquic.dylib 0xb0370 quic_conn_process_inbound + 1840 7 Network 0x323c40 nw_protocol_data_access_buffer + 840 8 libquic.dylib 0xb6b48 __quic_conn_handle_inbound_block_invoke + 168 9 libquic.dylib 0xb690c quic_conn_handle_inbound + 124 10 Network 0x3102ac __nw_protocol_implementation_get_input_internal_block_invoke + 136 11 Network 0x30f8d4 nw_protocol_implementation_read + 408 12 Network 0x30f128 nw_protocol_implementation_input_available + 96 13 Network 0x1f5c4 nw_channel_update_input_source(nw_channel*, nw_protocol*, bool) + 7024 14 Network 0x2d880 nw_channel_get_input_frames(nw_protocol*, nw_protocol*, unsigned int, unsigned int, unsigned int, nw_frame_array_s*) + 124 15 Network 0x911ac0 nw_protocol_ipv6_get_input_frames(nw_protocol*, nw_protocol*, unsigned int, unsigned int, unsigned int, nw_frame_array_s*) + 268 16 Network 0x1da8a4 nw_protocol_udp_get_input_frames(nw_protocol*, nw_protocol*, unsigned int, unsigned int, unsigned int, nw_frame_array_s*) + 208 17 Network 0x30f868 nw_protocol_implementation_read + 300 18 Network 0x3153e4 nw_protocol_implementation_get_input_frames + 148 19 Network 0x3572e0 nw_flow_service_reads(NWConcrete_nw_endpoint_handler*, NWConcrete_nw_endpoint_flow*, nw_flow_protocol*, bool) + 992 20 Network 0x363ef0 __nw_endpoint_handler_add_read_request_block_invoke + 452 21 Network 0x7ba20 nw_hash_table_apply + 2696 22 Network 0x34d84 nw_endpoint_handler_add_read_request + 1688 23 Network 0x34848 nw_endpoint_handler_add_read_request + 348 24 Network 0x936c nw_connection_add_read_request_on_queue + 232 25 Network 0x9204 nw_connection_add_read_request + 344 26 Network 0x8cc4 nw_connection_receive_internal + 140 27 CFNetwork 0xa7620 CFURLDownloadStart + 66020 28 CFNetwork 0xe194c _CFStreamErrorFromCFError + 24656 29 CFNetwork 0x101918 _CFStreamErrorFromCFError + 155676 30 CFNetwork 0xfe2d8 _CFStreamErrorFromCFError + 141788 31 CFNetwork 0x1008dc _CFStreamErrorFromCFError + 151520 32 CFNetwork 0x1344d4 _CFStreamErrorFromCFError + 363480 33 CFNetwork 0xa73c8 CFURLDownloadStart + 65420 34 CFNetwork 0xb68c0 CFURLDownloadStart + 128132 35 CFNetwork 0x135c5c _CFStreamErrorFromCFError + 369504 36 CFNetwork 0x34f18 CFHTTPMessageCopySerializedMessage + 47008 37 CFNetwork 0x1e8180 CFHTTPCookieStorageUnscheduleFromRunLoop + 225244 38 CFNetwork 0x13585c _CFStreamErrorFromCFError + 368480 39 CFNetwork 0xb4d9c CFURLDownloadStart + 121184 40 libdispatch.dylib 0x13250 _dispatch_block_async_invoke2 + 148 41 libdispatch.dylib 0x4300 _dispatch_client_callout + 20 42 libdispatch.dylib 0xb894 _dispatch_lane_serial_drain + 748 43 libdispatch.dylib 0xc3f8 _dispatch_lane_invoke + 432 44 libdispatch.dylib 0xd6a8 _dispatch_workloop_invoke + 1756 45 libdispatch.dylib 0x17004 _dispatch_root_queue_drain_deferred_wlh + 288 46 libdispatch.dylib 0x16878 _dispatch_workloop_worker_thread + 404 47 libsystem_pthread.dylib 0x1964 _pthread_wqthread + 288 48 libsystem_pthread.dylib 0x1a04 start_wqthread + 8
14
6
3.1k
Oct ’23
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?
3
0
1k
3w