6
votes

Server: Ubunta with nodejs express and nginx proxy.

When using my android app everything is working fine.

When using my iOS app i get:

"error calling GET on /district Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x1c0458db0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=http://xxx.xxx.xxx.xxx/api/district, NSErrorFailingURLKey=http://xxx.xxx.xxx.xxx/api/district, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}"

This error only happens when i do GET requests. I can login(POST) and add markers to my map etc. It all works. But getting data through get requests doesnt.

I think the the problem has to do with iOS having a 30sec timeout/keep alive on devices over iOS 6 or 7. It automatically sets a header to keep alive for 30 sec.

I had similar problems when working on my mac localhost server. But i fixed it by sending back a header with res.set("Connection", "close"); in my nodejs routes back to iOS. Like this:

nodejs districtRoutes.js

router.get('/api/district', passport.authenticate('jwt', { session:false }), function(req, res, next) {
  log.info("inside getting districts");
  District.find({}, function(err, district) {
    if (err){
      res.set("Connection", "close");
      log.info("error getting districts");
      res.status(404).json({message:"no districts found" + err});
      res.end();
    }else{
      res.set("Connection", "close");
      log.info("success getting districts");
      res.json(district);
      res.end();
    }
  });
});

swift iOS app. Inside httpRequester helper class

func getRequest (urlPath: String, body: [String:String], success:  @escaping (Data, HTTPURLResponse) -> ()) {
    let token: String! = KeychainWrapper.standard.string(forKey: "token")

    //check if url is a proper URL string
    guard let url = URL(string: baseUrl+urlPath) else {
        print("Error: cannot create URL")
        return
    }

    var urlRequest = URLRequest(url: url)
    urlRequest.httpMethod = "GET"
    urlRequest.addValue("JWT " + token, forHTTPHeaderField: "Authorization")
            //urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
            //urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

    let jsonData: Data
    do {
        jsonData = try JSONSerialization.data(withJSONObject: body)
        urlRequest.httpBody = jsonData
    } catch {
        print("Error: cannot create JSON from body")
        return
    }
    let task = URLSession.shared.dataTask(with: urlRequest) { (data: Data?, response: URLResponse?, error: Error?) in
        guard error == nil else {
            if let httpResponse = response as? HTTPURLResponse {
                self.errorHandling(response: httpResponse)
            }
            print("error calling GET on /" + urlPath)
            print(error!)
            return
        }
        guard let responseData = data else {
            print("Error: did not receive any data")
            return
        }
        let httpResponse = response as! HTTPURLResponse
        success(responseData, httpResponse)
    }

    task.resume()
}

I have the same code on my current ubunta server and as said, android works fine. So does post requests on iOS but not GET requests. The problem appears on booth wifi and 4g connection on iOS devices.

Let me know if there is anything else you need from me to help solve this problem.

Thanks!

1
I have read that post before posting myself. It didnt help me sadly!odd
Thanks. Though content length did not help.odd
@odd, did you find any solution for that?Ilesh P

1 Answers

0
votes

The solution was to not send body with a GET request. I knew it did not follow the request pattern for GET. Thought it was possible.

It was bad project planning on my part. One of my first own projects as a developer. All identifying information that is not secret should be kept inside the token. So when sending get requests the header with token has all the information needed to filter what data is sent back to the user.

Good luck and hope it helps!