Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$http request timeout not caught in error function on iOS 10 Cordova #15380

Closed
swaheed2 opened this issue Nov 9, 2016 · 17 comments
Closed

$http request timeout not caught in error function on iOS 10 Cordova #15380

swaheed2 opened this issue Nov 9, 2016 · 17 comments

Comments

@swaheed2
Copy link

swaheed2 commented Nov 9, 2016

Note: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.

Do you want to request a feature or report a bug?

bug

What is the current behavior?

Error function is not called when there is a timeout in the http request.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).

In iOS 10 Cordova application:

$http.get("http://10.255.255.1").then(function(res){
   console.log("res: " + res);
},function(err){
   console.log("err: " + err);
});

What is the expected behavior?

Error function to be called.

What is the motivation / use case for changing the behavior?

Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.

Angular 1.5.3
Cordova 6.3.1
Platform: iOS 10

Other information (e.g. stacktraces, related issues, suggestions how to fix)

Regular XMLHttpRequest works:

var xhr = new XMLHttpRequest(); xhr.onload = function () { 
    console.log("request finished") 
}; 
xhr.ontimeout = function (e) { 
    console.log("request timeout") 
}; 
xhr.open('GET', 'http://10.255.255.1', true);
xhr.send(null); 

Output: request timeout

http://stackoverflow.com/questions/40510603/angular-http-request-timeout-not-caught-in-error-function-on-ios-10-cordova

@swaheed2
Copy link
Author

swaheed2 commented Nov 9, 2016

XMLHttpRequest throws an error after 60 seconds even if you increase the timeout.

So it's a iOS 10 WebView thing which is not configurable I believe.

In Angular, If you supply a timeout property less than 60 seconds, an error is properly throw.

Still seems like a bug in Angular. It should still throw an error after request timeout by the WebView.

@gkalpak
Copy link
Member

gkalpak commented Nov 14, 2016

Thx for reporting this.

It has been already fixed with #14972 and the fix is included in 1.6.0-rc.0.
For 1.5.x it will be included in the upcoming 1.5.9 release.

@tipycalFlow
Copy link

tipycalFlow commented Jan 18, 2017

@gkalpak @swaheed2 An error is indeed thrown in case request takes more than 60s for iOS 10 but the response object doesn't contain any info to be sure that the error was because of a timeout or just an empty response received from server. This is the response I received:

{
	"data": null,
	"status": -1,
	"config": {
		"method": "GET",
		"transformRequest": [null],
		"transformResponse": [null],
		"url": "http://10.255.255.1",
		"headers": {
			"Accept": "application/json, text/plain, */*"
		}
	},
	"statusText": ""
}

Shouldn't the error object contain useful info like status: 408? I'm trying to handle this throughout my application elegantly with $httpProvider.interceptors. I've created a pull request that returns 408 as status. Let me know your thoughts on this.

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

The current behavior is intended. There is probably some confusion about different types of request timeouts.

408 means something different. It it a status sent form the server to indicate a timeout of the request, but also implies that the request did reach the server, which in turn implies other things, such as:

  • There is a network connection.
  • The server is running.

The timeouts we are talking about in this thread (which are indicated by a -1 status) are different: They are produced by the client. Such timeouts have different causes, including:

  • There is no network connection.
  • The server is down.
  • The server took too lond to respond.

BTW, empty responses/errors that come from the server will (typically) have a different status than -1, so they are easy to tell apart.

@tipycalFlow
Copy link

tipycalFlow commented Jan 18, 2017

I see your point. A case that is missing though is the force timing out of requests done by iOS 10 (after 60s) that does not give a status response (rather, there's no response at all, just a log in the console). So is it possible to provide an indication of timeout in the error callback, say in statusText or data or maybe a new param not in use currently?

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

A case that is missing though is the force timing out of requests done by iOS 10 (after 60s) that does not give a status response (rather, there's no response at all, just a log in the console).

Not sure what you mean. There should be a status of -1, which typically indicates a timeout an the error callback should get called as usual. Note that this has been fixed recently, so make sure you try it with a version that contain the fix (see #15380 (comment)).

@tipycalFlow
Copy link

tipycalFlow commented Jan 18, 2017

The response I'm talking about is the one received from server (not the client generated -1). Usually, as you mentioned, the server responds with a timeout, but for iOS 10, the browser cancels the request after 60 seconds without waiting for the server to respond back with the timeout (refer this issue that seems to have been resolved in iOS 10.2.1 beta 2 but this update is not out yet).

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

Sorry, I still don't get what you mean (it's probably me). The iOS issue you linked to is about cancelling the request after a timeout. This is done by the client and thus should have a -1 status. Are you seeing something different? Do I miss something?

@tipycalFlow
Copy link

tipycalFlow commented Jan 18, 2017

The issue I'm facing is that a particular http POST request in my application takes about 2-3 minutes to complete. The response from this request is received from server for all browsers except safari on iOS 10. In the developer view's network tab, I can see the response received from server for all browsers except Safari on iOS 10 (not for safari for mac though). On iOS safari, after exactly 60s (instead of 2-3 minutes), I see a log in the console that the request timed out and in the network tab, the request summary says 'no response received' (the same request had a response for other browsers). This is the issue mentioned in the link too and here's a link to the webkit bug: https://bugs.webkit.org/show_bug.cgi?id=163814. I hope I make sense now :)

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

OK, I understand what behavior you are seeing. As you already pointed out, this is non-Angular-related bug. What do you expect Angular to do differently that it currently does?

@tipycalFlow
Copy link

tipycalFlow commented Jan 18, 2017

My original dilemma is that in src/ng/httpBackend.js, all error events have the same handlers:

xhr.onerror = requestError;
xhr.onabort = requestError;
xhr.ontimeout = requestError;

which makes it difficult to differentiate between these possibilities in the callback. So is it possible to provide an indication of what kind of xhr error occurred in the error callback, say in statusText or data or maybe a new param not in use currently with a message like 'Request error'/'Request aborted'/'Request timed out'?

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

Oh, now I see what you mean 😃

It might be useful, although I wouldn't put too much value on what callback was called, because different browsers call different callbacks on each occasion.

Changing the response body would be the easiest, but it is a breaking change and I don't think it is worth it. Adding an extra property on the response object is a better idea, but needs a little more work.

If anyone is up for submitting a pull request, we might consider it. But like I said, don't read too much in what callback was called.

@tipycalFlow
Copy link

tipycalFlow commented Jan 19, 2017

@gkalpak could we send the info in the config param? like so:

var requestError = function() {
  completeRequest(callback, -1, null, 'XhrError: Request Error', '');
};

var requestAborted = function() {
  completeRequest(callback, -1, null, 'XhrError: Request aborted', '');
};

var requestTimedOut = function() {
  completeRequest(callback, -1, null, 'XhrError: Request timed out', '');
};

xhr.onerror = requestError;
xhr.onabort = requestAborted;
xhr.ontimeout = requestTimedOut;

I haven't tried it yet but this would be non-breaking ...

@gkalpak
Copy link
Member

gkalpak commented Jan 19, 2017

This is not sending it in the config param (you are setting a header). We can do a lot of things as an easy solution, but I prefer a clean one. Having such info in the config (or as a header) is not intuitive. It is a "property" of the response.

@tipycalFlow
Copy link

I tried adding a param called xhrStatus but soon realized that it would require a lot of changes in http.js, httpBackend.js and the specs. I'll try it later though: don't have enough time right now.

@gkalpak
Copy link
Member

gkalpak commented Jan 19, 2017

but soon realized that it would require a lot of changes in http.js, httpBackend.js and the specs

That's what I meant by:

Adding an extra property on the response object is a better idea, but needs a little more work.
😃

@tipycalFlow
Copy link

Here's a pull request with the changes as you've suggested. Please check if it's in line with what you had in mind and if it goes with the coding standards of the library.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants