I have a problem with my http request from my custom Api using URLSession in Xcode with Swift. This is my code:
let forecast = self.forecastList[forecastIndex]
let url : URL = URL(string: "https:laundryireland.tk/getForecast?pwd=\(self.CustomApiKey)&temp=\(forecast.temp)&hum=\(forecast.humidity)&pres=\(forecast.pressure)&weat=\(forecast.weather)&wind=\(forecast.windspeed)")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
do {
let scores = try JSONDecoder().decode(Scores.self, from: data)
// ...
} catch {
print("ops")
}
}
task.resume()
This code throw an error at the third line already:
2019-11-04 12:53:26.701212+0000 DIYP[2554:1119528] [] tcp_input [C6.1:3] flags=[R.] seq=0, ack=2949324956, win=0 state=SYN_SENT rcv_nxt=0, snd_una=2949324955
2019-11-04 12:53:26.702757+0000 DIYP[2554:1119528] Connection 6: received failure notification
2019-11-04 12:53:26.702843+0000 DIYP[2554:1119528] Connection 6: failed to connect 1:61, reason -1
2019-11-04 12:53:26.702897+0000 DIYP[2554:1119528] Connection 6: encountered error(1:61)
2019-11-04 12:53:26.706142+0000 DIYP[2554:1119528] Task <46A61430-630E-48F8-B121-82B1C7BEE5DF>.<2> HTTP load failed, 0/0 bytes (error code: -1004 [1:61])
I saw here: TIC TCP Conn Failed [4:0x604000360300]: 1:61 Err(61) <1> HTTP load failed (error code: -1004 that the error 1004 is something related to the device not able to resolve the host but this seems odd to me because my URL is on a registered domain running on a remote server which is accessible from everywhere (just type: http://laundryireland.tk in your browser to see that it's working).
How do I fix this issue?
So the problem was first of all a typo: my domain is http://laundryireland.tk while in the code I wrote https://laundryireland.tk .
I found out is still possible to change the app settings to allow http connections: right click on Info.plist, Open As > Source Code then copy paste the following code right after <dict> at the top:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Related
What's Expected:
Response with status code 200 and a JSON body with auth token etc.
What's Happening:
A device will (randomly?) no longer make the login call to auth0 successfully.
HTTP status code, response data will all be nil values.
-Other devices running the same app branch, same wifi network are able to make the call with no issue. Then after some time, generally 1-2 days the problem phone will no longer have any trouble making the call and will continue on as before the issue started.
Throws error Code 303 (CFNetworkErrors.cfErrorHTTPParseFailure) in logs. Apple docs say it's a failure to parse the response from the server. I believe it's because the response data is null.
App Version:
This issue has shown up so far on iOS devices running 14.6 or higher.
Testing:
Used both an AlamoFire based and URLSession based network call and both can have the issue. The example code below is using the URLSession instead of the AlamoFire, but switching does not seem to have an effect on stopping devices from being able to develop the issue.
Changing from WiFi to Celluar internet has no effect, changing to another WiFi network has no effect either.
Deleting the App and reinstalling has no effect.
Because I have not figured out what triggers the issue I have only got my hands on a device with the issue a few times before it resolved itself and there was nothing to test.
I have managed to save some logs (print statements and some Firebase Analytics console messages). The segments I think are useful are pasted below.
Network call Code:
func makeNetworkCall(url: String, method: HTTPMethod = .get, parameters: Parameters? = nil, headers: HTTPHeaders? = nil, timeout: Double, completion: #escaping (DefaultURLRequestReturnModel) -> Void ) {
logger.debug("makingCall with: url= _\(url)_ method= _\(method)_, param= _\(parameters)_, headers= _\(headers)_")
var urlRequest: URLRequest?
var hasReturned: Bool = false
DispatchQueue.main.asyncAfter(deadline: .now() + timeout, execute: {
if !hasReturned {
completion(DefaultURLRequestReturnModel(isSuccess: false, statusCode: ErrorCodes.timeoutError.rawValue, action: .failure, dataResponse: nil))
}
})
do {
urlRequest = try URLRequest(url: url, method: method, headers: headers)
if let parameters = parameters {
urlRequest?.httpBody = try JSONSerialization.data(withJSONObject: parameters)
}
} catch {
completion(DefaultURLRequestReturnModel(isSuccess: false, statusCode: ErrorCodes.encodeError.rawValue, action: .failure, dataResponse: nil))
return
}
// Unwrap the optional urlRequest to confirm its != nil
guard var safeURLRequest = urlRequest else {
completion(DefaultURLRequestReturnModel(isSuccess: false, statusCode: ErrorCodes.encodeError.rawValue, action: .failure, dataResponse: nil))
return
}
if safeURLRequest.httpBody != nil {
safeURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
let session = URLSession.shared
logger.debug("before session.dataTask for url \(safeURLRequest.url)")
let task = session.dataTask(with: safeURLRequest, completionHandler: { data, response, error in
logger.debug("url session completion for safeURLRequest.url _\(safeURLRequest.url)_, response= _\(response)_, error= _\(error.debugDescription)_, data= _\(data)_")
if let httpResponse = response as? HTTPURLResponse {
logger.debug("url session completion for URL \(safeURLRequest.url): status \(httpResponse.statusCode)")
switch httpResponse.statusCode {
case 200, 204:
let defaultDataResponse = DefaultDataResponse(request: safeURLRequest, response: httpResponse, data: data, error: error)
completion(DefaultURLRequestReturnModel(isSuccess: true, statusCode: httpResponse.statusCode, action: httpResponse.statusCode == 200 ? .success : .successEmpty, dataResponse: defaultDataResponse))
default:
logger.debug("ON ERROR")
completion(DefaultURLRequestReturnModel(isSuccess: false, statusCode: httpResponse.statusCode, action: httpResponse.statusCode == 401 ? .authError : .failure, dataResponse: nil))
}
} else {
// This is Trigged in the error logs
logger.debug("url session completion for URL \(safeURLRequest.url): response = \(response) __ can't be cast to HTTPURLResponse")
if NetworkManager.isConnectedToInternet() { logger.networkNilEvent() }
completion(DefaultURLRequestReturnModel(isSuccess: false, statusCode: ErrorCodes.noResponseError.rawValue, action: .failure, dataResponse: nil))
}
hasReturned = true
})
task.resume()
}
Console Logs From A Problem Device:
debug: url session completion for safeURLRequest.url
Optional(https://cpht.auth0.com/oauth/ro), response= nil, error= _Optional(Error Domain=kCFErrorDomainCFNetwork Code=303 "(null)" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask
.<9>,
_kCFStreamErrorDomainKey=4, NSErrorPeerAddressKey=<CFData 0x28375a080 [0x1f9151860]>{length = 16, capacity = 16, bytes =
0x100201bb6810b8f80000000000000000}, _kCFStreamErrorCodeKey=-2205,
NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask .<9>" )}), data= nil debug: url session completion for URL
Optional(https://cpht.auth0.com/oauth/ro): response = nil __ can't be
cast to HTTPURLResponse
Firebase Analytics Logs From A Problem Device:
I have replaced the IP Addresses from something like 100.0.0.0:443 to IP:443 in the following logs.
App name has been changed to MyAppName
2021-08-12 16:59:51.615734-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.615828-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.624933-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.625051-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.625570-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.625716-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_adaptive_read_handler [C14 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for read_timeout failed
2021-08-12 16:59:51.625773-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_adaptive_write_handler [C14 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for write_timeout failed
2021-08-12 16:59:51.625828-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_keepalive_handler [C14 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for keepalive failed
2021-08-12 16:59:51.626028-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.626859-0500 MyAppName[4586:1910198] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 10909f7c8
2021-08-12 16:59:51.627061-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_adaptive_read_handler [C13.2.1 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for read_timeout failed
2021-08-12 16:59:51.627118-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_adaptive_write_handler [C13.2.1 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for write_timeout failed
2021-08-12 16:59:51.627257-0500 MyAppName[4586:1910198] [connection] nw_endpoint_handler_set_keepalive_handler [C13.2.1 IP:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for keepalive failed
2021-08-12 16:59:51.641425-0500 MyAppName[4586:1909925] [] nw_protocol_instance_access_flow_state [C13.2.1:2] Failed to find flow 1090aae88
2021-08-12 16:59:51.647736-0500 MyAppName[4586:1909925] Task .<9> HTTP load failed, 529/0 bytes (error code: 303 [4:-2205])
2021-08-12 16:59:51.648181-0500 MyAppName[4586:1909923] Task .<9> finished with error [303] Error Domain=kCFErrorDomainCFNetwork Code=303 "(null)" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask .<9>, _kCFStreamErrorDomainKey=4, NSErrorPeerAddressKey=<CFData 0x28375a080 [0x1f9151860]>{length = 16, capacity = 16, bytes = 0x100201bb6810b8f80000000000000000}, _kCFStreamErrorCodeKey=-2205, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask .<9>"
)}
2021-08-12 16:59:51.648326-0500 MyAppName[4586:1909925] [connection] nw_endpoint_handler_unregister_context [C13.2.1 IP:443 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] Cannot unregister after flow table is released
2021-08-12 16:59:51.648393-0500 MyAppName[4586:1909925] [h3connection] 0x1099d1a18 13 closed with peer error 258
2021-08-12 16:59:51.648500-0500 MyAppName[4586:1909925] [connection] nw_endpoint_handler_add_write_request [C13.2.1 IP:443 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] Cannot send after flow table is released
2021-08-12 16:59:51.648625-0500 MyAppName[4586:1909925] [connection] nw_write_request_report [C13] Send failed with error "Socket is not connected"
Thank you for any of your ideas on fixing this issue!
We had the same issuie that you described. In our case, one of our custom request headers exceeded the max header size limit of the webserver. Nginx has a max header size around 8kb and will return a 413 error to the client if you exceed this limit. for whatever reason, the ios app will not parse this response and throw a 303 error. After we increased the http2_max_header_size to 32kb, it worked.
I've developed an app in SwiftUI where I also use a chat with Parse LiveQuery.
The chat is working perfectly in real time until the app goes in background or the phone screen is closed.
If I receive a notification for a new message and I open the app from the notification then the LiveQuery stopes working and I recieve the error : "Parse LiveQuery: Error processing message: Optional(POSIXErrorCode: Software caused connection abort)"
I register the Message class
class Message: PFObject, PFSubclassing {
#NSManaged var message : String
#NSManaged var receiver : PFObject
#NSManaged var thread_id : PFObject
#NSManaged var produs : PFObject
#NSManaged var sender : PFObject
#NSManaged var picture : PFObject
class func parseClassName() -> String {
return "Message"
}
}
On the ChatView I have .onReceive where I start the function for chat and liveQuery
var messagesQuery: PFQuery<Message> {
return (Message.query()?
.whereKey("thread_id", equalTo: self.mainThread_obj)
.order(byDescending: "createdAt")) as! PFQuery<Message>
}
self.StartMessagesLiveQuery(messagesQuery: messagesQuery)
The func StartMessagesLiveQuery is:
let subscription: Subscription<Message> = Client.shared.subscribe(messagesQuery)
subscription.handle(Event.created) { query, object in
print("Live Query Trigered")
print(object)
DispatchQueue.global(qos: .background).async {
///reading object and appending to array}}
The only problem is when the app is opened from Notification.
I've there any way to force ParseLiveQuery to reconnect?
Error stack:
2021-03-03 10:15:47.442021+0200 App[35496:6350370] [connection] nw_read_request_report [C2] Receive failed with error "Software caused connection abort"
2021-03-03 10:15:47.449044+0200 App[35496:6350370] [connection] nw_read_request_report [C3] Receive failed with error "Software caused connection abort"
2021-03-03 10:15:47.449291+0200 App[35496:6350370] [connection] nw_read_request_report [C1] Receive failed with error "Software caused connection abort"
2021-03-03 10:15:47.455008+0200 App[35496:6350370] [connection] nw_flow_add_write_request [C2.1 52.1.38.170:443 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] cannot accept write requests
2021-03-03 10:15:47.455055+0200 App[35496:6350370] [connection] nw_write_request_report [C2] Send failed with error "Socket is not connected"
2021-03-03 10:15:47.455815+0200 App[35496:6350657] ParseLiveQuery: Error processing message: Optional(POSIXErrorCode: Software caused connection abort)
2021-03-03 10:15:47.456612+0200 App[35496:6350370] Task <F6A87098-D5D3-444B-B656-D1999D5089D1>.<44> HTTP load failed, 42/0 bytes (error code: -1005 [1:53])
2021-03-03 10:15:47.462120+0200 App[35496:6350657] Task <F6A87098-D5D3-444B-B656-D1999D5089D1>.<44> finished with error [-1005] Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=53, NSUnderlyingError=0x282d11830 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x280174aa0 [0x20a814660]>{length = 16, capacity = 16, bytes = 0x100201bb344692570000000000000000}, _kCFStreamErrorCodeKey=53, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <F6A87098-D5D3-444B-B656-D1999D5089D1>.<44>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <F6A87098-D5D3-444B-B656-D1999D5089D1>.<44>"
), NSLocalizedDescription=The network connection was lost., NSErrorFailingURLStringKey=https://site.b4a.app/classes/_User, NSErrorFailingURLKey=https://site.b4a.app/classes/_User, _kCFStreamErrorDomainKey=1}
2021-03-03 10:15:47.462237+0200 App[35496:6350657] [Error]: The network connection was lost. (Code: 100, Version: 1.19.1)
2021-03-03 10:15:47.462335+0200 Bazar[35496:6350657] [Error]: Network connection failed. Making attempt 1 after sleeping for 1.588128 seconds.
After this the network connection is restored and the app is functional, only the LiveQuery are not working.
I think i’ve solved it.
I've modified function StartMessagesLiveQuery:
func StartMessagesLiveQuery(messagesQuery: PFQuery<Message>){}
Inside the function I had:
let subscription: Subscription<Message> = Client.shared.subscribe(messagesQuery)
subscription!.handle(Event.created) { query, object in ... }
Now i’ve modified and put inside the Class
var subscription: Subscription<Message>?
var subscriber: ParseLiveQuery.Client!
and in the function i’ve modified with:
subscriber = ParseLiveQuery.Client()
subscription = subscriber.subscribe(messagesQuery)
I have a frontend Swift application in which users are to upload large videos and photos to Firebase Storage. I am currently working on error handling. The documentation does a good job of explaining error handling from Google's sever side of things, however it does not cover how to deal with connection loss.
This is the error handling directly from the documentation:
...
// Upload file and metadata to the object 'images/mountains.jpg'
let uploadTask = storageRef.putFile(from: localFile, metadata: metadata)
uploadTask.observe(.failure) { snapshot in
if let error = snapshot.error as? NSError {
switch (StorageErrorCode(rawValue: error.code)!) {
case .objectNotFound:
// File doesn't exist
break
case .unauthorized:
// User doesn't have permission to access file
break
case .cancelled:
// User canceled the upload
break
/* ... */
case .unknown:
// Unknown error occurred, inspect the server response
break
default:
// A separate error occurred. This is a good place to retry the upload.
break
}
}
}
I have done some tests on my device where the upload begins with no network connection. The following gets automatically printed to the console every second or so after the network goes down:
2020-07-06 01:38:28.361559-0700 Rage[12281:1978025] Connection 9: failed to connect 1:50, reason -1
2020-07-06 01:38:28.361648-0700 Rage[12281:1978025] Connection 9: encountered error(1:50)
2020-07-06 01:38:28.366906-0700 Rage[12281:1978025] Task <320E9387-F725-4AEA-B3BE-76425F73F9F7>.<6> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2020-07-06 01:38:28.368381-0700 Rage[12281:1978042] Task <320E9387-F725-4AEA-B3BE-76425F73F9F7>.<6> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x280a581e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <320E9387-F725-4AEA-B3BE-76425F73F9F7>.<6>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <320E9387-F725-4AEA-B3BE-76425F73F9F7>.<6>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/rage-5940.appspot.com/o/event%2FBEC3B24F-5F97-4B6A-8FD3-5DC2F7D37AFB.mp4?uploadType=resumable&name=event%2FBEC3B24F-5F97-4B6A-8FD3-5DC2F7D37AFB.mp4, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/rage-5940.appspot.com/o/event%2FBEC3B24F-5F97-4B6A-8FD3-5DC2F7D37AFB.mp4?uploadType=resumable&name=event%2FBEC3B24F-5F97-4B6A-8FD3-5DC2F7D37AFB.mp4, _kCFStreamErrorDomainKey=1}
Is this Firebase throwing these errors? If so they are probably being thrown trying to create a storageRef before the uploadTask could even begin, and thus escape any provided error handling.
Is there anyway to catch these network errors?
Did you try this?
if error._code == NSURLErrorNetworkConnectionLost {
}
there are a couple of more you can check with them:
NSURLErrorTimedOut
NSURLErrorNotConnectedToInternet
NSURLErrorCannotConnectToHost
I trying to make a https call to my web service from a ios application.
So I used:
//...
func sendMessage() {
let defaults = UserDefaults.standard
let url = URL(string: defaults.string(forKey:"host")! + ":" + defaults.string(forKey:"port")! + "/garage")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: String] = [
"identifier": defaults.string(forKey: "secret")!
]
request.httpBody = parameters.percentEncoded()
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
let task = session.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
let responseString = String(data: data, encoding: .utf8)
debugPrint("responseString = \(responseString)")
}
task.resume()
}
//...
extension ViewController: URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
//Trust the certificate even if not valid
let urlCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, urlCredential)
}
}
I installed the ca at on the target system too. But I also try to ignore the certificate.
I always get following error instead of a response:
2020-07-03 14:41:27.742361+0200 GarageOpener[5177:223905] ATS failed system trust
2020-07-03 14:41:27.742484+0200 GarageOpener[5177:223905] Connection 1: system TLS Trust evaluation failed(-9802)
2020-07-03 14:41:27.742642+0200 GarageOpener[5177:223905] Connection 1: TLS Trust encountered error 3:-9802
2020-07-03 14:41:27.742776+0200 GarageOpener[5177:223905] Connection 1: encountered error(3:-9802)
2020-07-03 14:41:27.746709+0200 GarageOpener[5177:223905] Task <63EC553D-485C-478D-813B-AF0C1D3D3223>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
2020-07-03 14:41:27.749778+0200 GarageOpener[5177:223905] Task <63EC553D-485C-478D-813B-AF0C1D3D3223>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x600002b187e0>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7fd6f2035600) s: *.mydomain.de i: *.mydomain.de>"
), NSUnderlyingError=0x600001773cf0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x600002b187e0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7fd6f2035600) s: *.mydomain.de i: *.mydomain.de>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://subdomain.mydomain.de:443/garage, NSErrorFailingURLStringKey=https://subdomain.mydomain.de:443/garage, NSErrorClientCertificateStateKey=0}
"3"
error Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x600002b187e0>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7fd6f2035600) s: *.mydomain.de i: *.mydomain.de>"
), NSUnderlyingError=0x600001773cf0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x600002b187e0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7fd6f2035600) s: *.mydomain.de i: *.mydomain.de>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://subdomain.mydomain.de:443/garage, NSErrorFailingURLStringKey=https://subdomain.mydomain.de:443/garage, NSErrorClientCertificateStateKey=0}
I know ignore isn't a good idea and it would allow man in the middle attacks. But at least the connection would be encrypted. And if somebody has the ability to make a man in the middle attack in my WLAN I have bigger problems than this.
Please help ;)
Okay, maybe it's time to answer my question.
The first I did wrong was the signing of the certificate.
I just set the domain/ip-address of the server in the Common Name (CN) entry of the certificate.
That is an outdated solution. You need to set the subjectAltName for modern applications: http://wiki.cacert.org/FAQ/subjectAltName .
But it feels very ugly to use the instructions from that wiki site to self-sign a certificate. So I used the awesome tool certstrap: https://github.com/square/certstrap
After that:
I passed the server.key and server.crt (private and signed public
key) to the server and configured everything fine.
I downloaded the CA (ca.crt) that I created (on the iphone).
You need to go to the settings->general->profiles and trust your
certificate.
Finally, you need to trust the certificate again:
https://support.apple.com/en-us/HT204477#:~:text=If%20you%20want%20to%20turn,Mobile%20Device%20Management%20(MDM).
Done. My application worked!
What I'm doing?
I'm working on a iOS app which is going to download files from an FTPS server. For this purpose I'm using the library FilesProvider.
Error description
Until now I successfully achieve to login in the server, list files and search for files, but I'm getting the following error when trying to download one of the files:
File Provider <FilesProvider.FTPFileProvider: 0x283c23900> shouldDoOperation Copy with action Copying and destination file:///private/var/mobile/Containers/Data/Application/90AF4202-18C1-4A41-B461-4FB262FD39B9/tmp/B13A8110-C919-48E4-8BD9-E684929310C0.tmp
2020-05-27 14:35:39.372289+0200 MyApp[548:100799] [] nw_socket_handle_socket_event [C13:1] Socket SO_ERROR [54: Connection reset by peer]
2020-05-27 14:35:39.595959+0200 MyApp[548:99892] CFNetwork SSLHandshake failed (-9806)
2020-05-27 14:35:39.596380+0200 MyApp[548:99892] TCP Conn 0x28274f540 SSLHandshake failed (-9806)
File Provider <FilesProvider.FTPFileProvider: 0x283c23900> Failed for operation Copy with action Copying and destination file:///private/var/mobile/Containers/Data/Application/90AF4202-18C1-4A41-B461-4FB262FD39B9/tmp/B13A8110-C919-48E4-8BD9-E684929310C0.tmp
Throwing Error: Error Domain=NSOSStatusErrorDomain Code=-9806 "(null)" UserInfo={_kCFStreamErrorCodeKey=-9806, _kCFStreamErrorDomainKey=3}
FTPFileProvider is an object created with the library I've mention above that handles the FTP connection. That provider looks like:
guard let url = URL(string: "ftps://X.X.X.X") else { return } // I have to use an IP address instead of a domain
var provider = FTPFileProvider(baseURL: url, mode: .default, credential: credential, cache: .none)
provider.delegate = self
provider.fileOperationDelegate = self // This delegate is only for print the first line of the error
provider.serverTrustPolicy = .disableEvaluation
After creating the provider of the connection, I've been able to do login in the server, search some files and get the file list. I'm doing that with this function:
provider.searchFiles(path: remotePath, recursive: false, query: predicate, foundItemHandler: { (file) in print("File found with name: \(file.name)") }, completionHandler: { (list, error) in
if error != nil {
DispatchQueue.main.async {
onError(error!)
}
} else {
var files:[String] = []
for f in list {
(f.isRegularFile) ? files.append(f.name) : nil
}
DispatchQueue.main.async {
onSucess(files)
}
}
})
When running that search I get this warning:
2020-05-27 14:45:51.831812+0200 MyApp[555:102153] [] nw_socket_handle_socket_event [C4:1] Socket SO_ERROR [54: Connection reset by peer]
But I successfully get an output in onSuccess(files). The returned value for files is:
["20200527-093234-28346646454.pdf", "20200527-105409-28346646454.pdf"]
After that search, I try to download one of the files is when I get the error describe at the beginning of this post. For do the download I have the following function:
provider.copyItem(path: "\(remotePath)/\(file)", to: localPath.absoluteString, overwrite: true) { (error) in
if error != nil {
DispatchQueue.main.async {
onError(error!)
}
} else {
DispatchQueue.main.async {
onSuccess(localPath)
}
}
}
What I've try
As you could see above, the object who connect to the server has disabled the SSL certificate checks. That's why I can do the login and search for the files.
I've configure the Info.plist disabling ATS:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key> <!-- Because I wan't to allow everything -->
<true/>
<key>NSAllowsLocalNetworking</key> <!-- Because seems like library uses AVFoundation framework -->
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key> <!-- Because I'm using a public IP instead a domain -->
<true/>
</dict>
Neither of that options works.
Some facts
Domain DNS entry is not going to be created for now. I can't do anything about this.
Server do have a valid SSL certificate.
I can successfully login, list files and search for files. (I just can't download)
I didn't try to upload a file. App isn't going to do it.
Questions
Why login, listing or searching files works but I get that error when trying to download?
Any idea on how to fix it? Any workaround?