diff options
| -rw-r--r-- | src/ng/httpBackend.js | 24 | ||||
| -rw-r--r-- | test/ng/httpBackendSpec.js | 53 | 
2 files changed, 66 insertions, 11 deletions
diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js index a3f6cdc0..69ac5976 100644 --- a/src/ng/httpBackend.js +++ b/src/ng/httpBackend.js @@ -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;    }  } diff --git a/test/ng/httpBackendSpec.js b/test/ng/httpBackendSpec.js index a7935a7c..da4fed16 100644 --- a/test/ng/httpBackendSpec.js +++ b/test/ng/httpBackendSpec.js @@ -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    });  | 
