mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
feat($httpBackend): add timeout support for JSONP requests
Documentation implies that timeout works for all requests, though it only works with XHR. To implement: - Change $httpBackend to set a timeout for JSONP requests which will immediately resolve the request when fired. - Cancel the timeout when requests are completed.
This commit is contained in:
parent
fc25a443f8
commit
cda7b71146
2 changed files with 66 additions and 11 deletions
|
|
@ -33,6 +33,7 @@ function $HttpBackendProvider() {
|
|||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
|
||||
// TODO(vojta): fix the signature
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
||||
var status;
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
url = url || $browser.url();
|
||||
|
||||
|
|
@ -42,12 +43,12 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|||
callbacks[callbackId].data = data;
|
||||
};
|
||||
|
||||
jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
||||
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
||||
function() {
|
||||
if (callbacks[callbackId].data) {
|
||||
completeRequest(callback, 200, callbacks[callbackId].data);
|
||||
} else {
|
||||
completeRequest(callback, -2);
|
||||
completeRequest(callback, status || -2);
|
||||
}
|
||||
delete callbacks[callbackId];
|
||||
});
|
||||
|
|
@ -58,8 +59,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|||
if (value) xhr.setRequestHeader(key, value);
|
||||
});
|
||||
|
||||
var status;
|
||||
|
||||
// In IE6 and 7, this might be called synchronously when xhr.send below is called and the
|
||||
// response is in the cache. the promise api will ensure that to the app code the api is
|
||||
// always async
|
||||
|
|
@ -105,13 +104,14 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|||
}
|
||||
|
||||
xhr.send(post || '');
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
$browserDefer(function() {
|
||||
status = -1;
|
||||
xhr.abort();
|
||||
}, timeout);
|
||||
}
|
||||
if (timeout > 0) {
|
||||
var timeoutId = $browserDefer(function() {
|
||||
status = -1;
|
||||
jsonpDone && jsonpDone();
|
||||
xhr && xhr.abort();
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -119,6 +119,9 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|||
// URL_MATCH is defined in src/service/location.js
|
||||
var protocol = (url.match(SERVER_MATCH) || ['', locationProtocol])[1];
|
||||
|
||||
// cancel timeout
|
||||
timeoutId && $browserDefer.cancel(timeoutId);
|
||||
|
||||
// fix status code for file protocol (it's always 0)
|
||||
status = (protocol == 'file') ? (response ? 200 : 404) : status;
|
||||
|
||||
|
|
@ -152,5 +155,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|||
}
|
||||
|
||||
rawDocument.body.appendChild(script);
|
||||
return doneWrapper;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,36 @@
|
|||
describe('$httpBackend', function() {
|
||||
|
||||
var $backend, $browser, callbacks,
|
||||
xhr, fakeDocument, callback;
|
||||
xhr, fakeDocument, callback,
|
||||
fakeTimeoutId = 0;
|
||||
|
||||
// TODO(vojta): should be replaced by $defer mock
|
||||
function fakeTimeout(fn, delay) {
|
||||
fakeTimeout.fns.push(fn);
|
||||
fakeTimeout.delays.push(delay);
|
||||
fakeTimeout.ids.push(++fakeTimeoutId);
|
||||
return fakeTimeoutId;
|
||||
}
|
||||
|
||||
fakeTimeout.fns = [];
|
||||
fakeTimeout.delays = [];
|
||||
fakeTimeout.ids = [];
|
||||
fakeTimeout.flush = function() {
|
||||
var len = fakeTimeout.fns.length;
|
||||
fakeTimeout.delays = [];
|
||||
fakeTimeout.ids = [];
|
||||
while (len--) fakeTimeout.fns.shift()();
|
||||
};
|
||||
fakeTimeout.cancel = function(id) {
|
||||
var i = indexOf(fakeTimeout.ids, id);
|
||||
if (i >= 0) {
|
||||
fakeTimeout.fns.splice(i, 1);
|
||||
fakeTimeout.delays.splice(i, 1);
|
||||
fakeTimeout.ids.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
|
|
@ -102,6 +117,27 @@ describe('$httpBackend', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should cancel timeout on completion', function() {
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
$backend('GET', '/url', null, callback, {}, 2000);
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
spyOn(xhr, 'abort');
|
||||
|
||||
expect(fakeTimeout.delays[0]).toBe(2000);
|
||||
|
||||
xhr.status = 200;
|
||||
xhr.readyState = 4;
|
||||
xhr.onreadystatechange();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
expect(fakeTimeout.delays.length).toBe(0);
|
||||
expect(xhr.abort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should register onreadystatechange callback before sending', function() {
|
||||
// send() in IE6, IE7 is sync when serving from cache
|
||||
function SyncXhr() {
|
||||
|
|
@ -239,6 +275,21 @@ describe('$httpBackend', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should abort request on timeout', function() {
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(status).toBe(-1);
|
||||
});
|
||||
|
||||
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback, null, 2000);
|
||||
expect(fakeDocument.$$scripts.length).toBe(1);
|
||||
expect(fakeTimeout.delays[0]).toBe(2000);
|
||||
|
||||
fakeTimeout.flush();
|
||||
expect(fakeDocument.$$scripts.length).toBe(0);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
// TODO(vojta): test whether it fires "async-start"
|
||||
// TODO(vojta): test whether it fires "async-end" on both success and error
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue