diff options
| -rw-r--r-- | src/service/http.js | 56 | ||||
| -rw-r--r-- | test/service/httpSpec.js | 67 |
2 files changed, 98 insertions, 25 deletions
diff --git a/src/service/http.js b/src/service/http.js index 23f33fad..d7ad9dde 100644 --- a/src/service/http.js +++ b/src/service/http.js @@ -92,7 +92,7 @@ function $HttpProvider() { this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope', function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope) { - var cache = $cacheFactory('$http'); + var defaultCache = $cacheFactory('$http'); // the actual service function $http(config) { @@ -226,7 +226,7 @@ function $HttpProvider() { * Represents Request object, returned by $http() * * !!! ACCESS CLOSURE VARS: - * $httpBackend, $browser, $config, $log, $rootScope, cache, $http.pendingRequests + * $httpBackend, $browser, $config, $log, $rootScope, defaultCache, $http.pendingRequests */ function XhrFuture() { var rawRequest, parsedHeaders, @@ -244,9 +244,15 @@ function $HttpProvider() { // aborted request or jsonp if (!rawRequest) parsedHeaders = {}; - if (cfg.cache && cfg.method == 'GET' && 200 <= status && status < 300) { - parsedHeaders = parsedHeaders || parseHeaders(rawRequest.getAllResponseHeaders()); - cache.put(cfg.url, [status, response, parsedHeaders]); + if (cfg.cache && cfg.method == 'GET') { + var cache = isObject(cfg.cache) && cfg.cache || defaultCache; + if (200 <= status && status < 300) { + parsedHeaders = parsedHeaders || parseHeaders(rawRequest.getAllResponseHeaders()); + cache.put(cfg.url, [status, response, parsedHeaders]); + } else { + // remove future object from cache + cache.remove(cfg.url); + } } fireCallbacks(response, status); @@ -333,19 +339,43 @@ function $HttpProvider() { headers = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, defHeaders.common, defHeaders[lowercase(cfg.method)], cfg.headers); - var fromCache; - if (cfg.cache && cfg.method == 'GET' && (fromCache = cache.get(cfg.url))) { - $browser.defer(function() { - parsedHeaders = fromCache[2]; - fireCallbacks(fromCache[1], fromCache[0]); - }); - } else { + var cache = isObject(cfg.cache) && cfg.cache || defaultCache, + fromCache; + + if (cfg.cache && cfg.method == 'GET') { + fromCache = cache.get(cfg.url); + if (fromCache) { + if (fromCache instanceof XhrFuture) { + // cached request has already been sent, but there is no reponse yet, + // we need to register callback and fire callbacks when the request is back + // note, we have to get the values from cache and perform transformations on them, + // as the configurations don't have to be same + fromCache.on('always', function() { + var requestFromCache = cache.get(cfg.url); + parsedHeaders = requestFromCache[2]; + fireCallbacks(requestFromCache[1], requestFromCache[0]); + }); + } else { + // serving from cache - still needs to be async + $browser.defer(function() { + parsedHeaders = fromCache[2]; + fireCallbacks(fromCache[1], fromCache[0]); + }); + } + } else { + // put future object into cache + cache.put(cfg.url, self); + } + } + + // really send the request + if (!cfg.cache || cfg.method !== 'GET' || !fromCache) { rawRequest = $httpBackend(cfg.method, cfg.url, data, done, headers, cfg.timeout); } $rootScope.$broadcast('$http.request', self); $http.pendingRequests.push(self); - return this; + return self; }; // just alias so that in stack trace we can see send() instead of retry() diff --git a/test/service/httpSpec.js b/test/service/httpSpec.js index 8212eb07..a235426e 100644 --- a/test/service/httpSpec.js +++ b/test/service/httpSpec.js @@ -779,16 +779,22 @@ describe('$http', function() { describe('cache', function() { + var cache; + + beforeEach(inject(function($cacheFactory) { + cache = $cacheFactory('testCache'); + })); + function doFirstCacheRequest(method, respStatus, headers) { $httpBackend.expect(method || 'GET', '/url').respond(respStatus || 200, 'content', headers); - $http({method: method || 'GET', url: '/url', cache: true}); + $http({method: method || 'GET', url: '/url', cache: cache}); $httpBackend.flush(); } - it('should cache GET request', function() { + it('should cache GET request when cache is provided', function() { doFirstCacheRequest(); - $http({method: 'get', url: '/url', cache: true}).on('200', callback); + $http({method: 'get', url: '/url', cache: cache}).on('200', callback); $browser.defer.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -796,11 +802,28 @@ describe('$http', function() { }); + it('should not cache when cache is not provided', function() { + doFirstCacheRequest(); + + $httpBackend.expect('GET', '/url').respond(); + $http({method: 'GET', url: '/url'}); + }); + + + it('should perform request when cache cleared', function() { + doFirstCacheRequest(); + + cache.removeAll(); + $httpBackend.expect('GET', '/url').respond(); + $http({method: 'GET', url: '/url', cache: cache}); + }); + + it('should always call callback asynchronously', function() { doFirstCacheRequest(); - $http({method: 'get', url: '/url', cache: true}).on('200', callback); + $http({method: 'get', url: '/url', cache: cache}).on('200', callback); - expect(callback).not.toHaveBeenCalledOnce(); + expect(callback).not.toHaveBeenCalled(); }); @@ -808,7 +831,7 @@ describe('$http', function() { doFirstCacheRequest('POST'); $httpBackend.expect('POST', '/url').respond('content2'); - $http({method: 'POST', url: '/url', cache: true}).on('200', callback); + $http({method: 'POST', url: '/url', cache: cache}).on('200', callback); $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -820,7 +843,7 @@ describe('$http', function() { doFirstCacheRequest('PUT'); $httpBackend.expect('PUT', '/url').respond('content2'); - $http({method: 'PUT', url: '/url', cache: true}).on('200', callback); + $http({method: 'PUT', url: '/url', cache: cache}).on('200', callback); $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -832,7 +855,7 @@ describe('$http', function() { doFirstCacheRequest('DELETE'); $httpBackend.expect('DELETE', '/url').respond(206); - $http({method: 'DELETE', url: '/url', cache: true}).on('206', callback); + $http({method: 'DELETE', url: '/url', cache: cache}).on('206', callback); $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -843,7 +866,7 @@ describe('$http', function() { doFirstCacheRequest('GET', 404); $httpBackend.expect('GET', '/url').respond('content2'); - $http({method: 'GET', url: '/url', cache: true}).on('200', callback); + $http({method: 'GET', url: '/url', cache: cache}).on('200', callback); $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -858,7 +881,7 @@ describe('$http', function() { expect(headers('server')).toBe('Apache'); }); - $http({method: 'GET', url: '/url', cache: true}).on('200', callback); + $http({method: 'GET', url: '/url', cache: cache}).on('200', callback); $browser.defer.flush(); expect(callback).toHaveBeenCalledOnce(); }); @@ -870,10 +893,27 @@ describe('$http', function() { expect(status).toBe(201); }); - $http({method: 'get', url: '/url', cache: true}).on('2xx', callback); + $http({method: 'get', url: '/url', cache: cache}).on('2xx', callback); $browser.defer.flush(); expect(callback).toHaveBeenCalledOnce(); }); + + + it('should use cache even if request fired before first response is back', function() { + $httpBackend.expect('GET', '/url').respond(201, 'fake-response'); + + callback.andCallFake(function(response, status, headers) { + expect(response).toBe('fake-response'); + expect(status).toBe(201); + }); + + $http({method: 'GET', url: '/url', cache: cache}).on('always', callback); + $http({method: 'GET', url: '/url', cache: cache}).on('always', callback); + + $httpBackend.flush(); + expect(callback).toHaveBeenCalled(); + expect(callback.callCount).toBe(2); + }); }); @@ -903,10 +943,13 @@ describe('$http', function() { }); - it('should remove the request when served from cache', function() { + it('should update pending requests even when served from cache', function() { $httpBackend.when('GET').respond(200); $http({method: 'get', url: '/cached', cache: true}); + $http({method: 'get', url: '/cached', cache: true}); + expect($http.pendingRequests.length).toBe(2); + $httpBackend.flush(); expect($http.pendingRequests.length).toBe(0); |
