aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/service/http.js56
-rw-r--r--test/service/httpSpec.js67
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);