98 lines
3.5 KiB
JavaScript
98 lines
3.5 KiB
JavaScript
/*
|
|
Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
'use strict';
|
|
var globalOptions = require('../options');
|
|
var helpers = require('../helpers');
|
|
|
|
function networkFirst(request, values, options) {
|
|
options = options || {};
|
|
var successResponses = options.successResponses ||
|
|
globalOptions.successResponses;
|
|
// This will bypass options.networkTimeout if it's set to a false-y value like
|
|
// 0, but that's the sane thing to do anyway.
|
|
var networkTimeoutSeconds = options.networkTimeoutSeconds ||
|
|
globalOptions.networkTimeoutSeconds;
|
|
helpers.debug('Strategy: network first [' + request.url + ']', options);
|
|
|
|
return helpers.openCache(options).then(function(cache) {
|
|
var timeoutId;
|
|
var promises = [];
|
|
var originalResponse;
|
|
|
|
if (networkTimeoutSeconds) {
|
|
var cacheWhenTimedOutPromise = new Promise(function(resolve) {
|
|
timeoutId = setTimeout(function() {
|
|
cache.match(request).then(function(response) {
|
|
var cacheOptions = options.cache || globalOptions.cache;
|
|
// Only resolve this promise if there's a valid response in the
|
|
// cache. This ensures that we won't time out a network request
|
|
// unless there's a cached entry to fallback on, which is arguably
|
|
// the preferable behavior.
|
|
var now = Date.now();
|
|
var maxAgeSeconds = cacheOptions.maxAgeSeconds;
|
|
if (helpers.isResponseFresh(response, maxAgeSeconds, now)) {
|
|
resolve(response);
|
|
}
|
|
});
|
|
}, networkTimeoutSeconds * 1000);
|
|
});
|
|
promises.push(cacheWhenTimedOutPromise);
|
|
}
|
|
|
|
var networkPromise = helpers.fetchAndCache(request, options)
|
|
.then(function(response) {
|
|
// We've got a response, so clear the network timeout if there is one.
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
|
|
if (successResponses.test(response.status)) {
|
|
return response;
|
|
}
|
|
|
|
helpers.debug('Response was an HTTP error: ' + response.statusText,
|
|
options);
|
|
originalResponse = response;
|
|
throw new Error('Bad response');
|
|
}).catch(function(error) {
|
|
helpers.debug('Network or response error, fallback to cache [' +
|
|
request.url + ']', options);
|
|
return cache.match(request).then(function(response) {
|
|
// If there's a match in the cache, resolve with that.
|
|
if (response) {
|
|
return response;
|
|
}
|
|
|
|
// If we have a Response object from the previous fetch, then resolve
|
|
// with that, even though it corresponds to an error status code.
|
|
if (originalResponse) {
|
|
return originalResponse;
|
|
}
|
|
|
|
// If we don't have a Response object from the previous fetch, likely
|
|
// due to a network failure, then reject with the failure error.
|
|
throw error;
|
|
});
|
|
});
|
|
|
|
promises.push(networkPromise);
|
|
|
|
return Promise.race(promises);
|
|
});
|
|
}
|
|
|
|
module.exports = networkFirst;
|