I have a critical issue where my websocket will not connect to a server that is sitting behind an NGINX reverse proxy only on iOS 13. I have tested on both a real device and simulator with no success. It simply hangs on preparing. it never receives updates to the viabilityUpdateHandler
and only ever enters the preparing state of the stateUpdateHandler.
On any iOS greater or equal to iOS 14 it works seamlessly. I can connect to a local server that is not dealing with any certificates on iOS 13 no problem, but when my production server is in play it does not communicate something properly.
I am using NWConnection
's NWProtocolWebSocket
.
The setup is basic and straight forward
let options = NWProtocolWebSocket.Options()
options.autoReplyPing = configuration.autoReplyPing
options.maximumMessageSize = configuration.maximumMessageSize
if configuration.urlRequest != nil {
options.setAdditionalHeaders(configuration.urlRequest?.allHTTPHeaderFields?.map { ($0.key, $0.value) } ?? [])
_ = configuration.cookies.map { cookie in
options.setAdditionalHeaders([(name: cookie.name, value: cookie.value)])
}
}
if !configuration.headers.isEmpty {
options.setAdditionalHeaders(configuration.headers.map { ($0.key, $0.value) } )
}
let parameters: NWParameters = configuration.trustAll ? try TLSConfiguration.trustSelfSigned(
configuration.trustAll,
queue: configuration.queue,
certificates: configuration.certificates) : (configuration.url.scheme == "ws" ? .tcp : .tls)
parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
connection = NWConnection(to: .url(configuration.url), using: parameters)
The trust store is also straight forward
public static func trustSelfSigned(_
trustAll: Bool,
queue: DispatchQueue,
certificates: [String]?
) throws -> NWParameters {
let options = NWProtocolTLS.Options()
var secTrustRoots: [SecCertificate]?
secTrustRoots = try certificates?.compactMap({ certificate in
let filePath = Bundle.main.path(forResource: certificate, ofType: "der")!
let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
return SecCertificateCreateWithData(nil, data as CFData)!
})
sec_protocol_options_set_verify_block(
options.securityProtocolOptions,
{ _, sec_trust, sec_protocol_verify_complete in
guard !trustAll else {
sec_protocol_verify_complete(true)
return
}
let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
if let trustRootCertificates = secTrustRoots {
SecTrustSetAnchorCertificates(trust, trustRootCertificates as CFArray)
}
dispatchPrecondition(condition: .onQueue(queue))
SecTrustEvaluateAsyncWithError(trust, queue) { _, result, error in
if let error = error {
print("Trust failed: \(error.localizedDescription)")
}
print("Validation Result: \(result)")
sec_protocol_verify_complete(result)
}
},
queue
)
sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, .TLSv12)
let parameters = NWParameters(tls: options)
parameters.allowLocalEndpointReuse = true
parameters.includePeerToPeer = true
return parameters
}