Skip to content

Commit bf8c949

Browse files
authoredNov 6, 2020
networkTimeoutSeconds in NetworkOnly (#2620)
* networkTimeoutSeconds in NetworkOnly * Linting * Review feedback
1 parent d62c185 commit bf8c949

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed
 

‎packages/workbox-strategies/src/NetworkOnly.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88

99
import {assert} from 'workbox-core/_private/assert.js';
1010
import {logger} from 'workbox-core/_private/logger.js';
11+
import {timeout} from 'workbox-core/_private/timeout.js';
1112
import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
1213

13-
import {Strategy} from './Strategy.js';
14+
import {Strategy, StrategyOptions} from './Strategy.js';
1415
import {StrategyHandler} from './StrategyHandler.js';
1516
import {messages} from './utils/messages.js';
1617
import './_version.js';
1718

1819

20+
interface NetworkOnlyOptions extends Omit<StrategyOptions, 'cacheName' | 'matchOptions'> {
21+
networkTimeoutSeconds?: number;
22+
}
23+
1924
/**
2025
* An implementation of a
2126
* [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only}
@@ -30,6 +35,24 @@ import './_version.js';
3035
* @memberof module:workbox-strategies
3136
*/
3237
class NetworkOnly extends Strategy {
38+
private readonly _networkTimeoutSeconds: number;
39+
40+
/**
41+
* @param {Object} [options]
42+
* @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
43+
* to use in conjunction with this caching strategy.
44+
* @param {Object} [options.fetchOptions] Values passed along to the
45+
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
46+
* of all fetch() requests made by this strategy.
47+
* @param {number} [options.networkTimeoutSeconds] If set, any network requests
48+
* that fail to respond within the timeout will result in a network error.
49+
*/
50+
constructor(options: NetworkOnlyOptions = {}) {
51+
super(options);
52+
53+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
54+
}
55+
3356
/**
3457
* @private
3558
* @param {Request|string} request A request to run this strategy for.
@@ -42,15 +65,27 @@ class NetworkOnly extends Strategy {
4265
assert!.isInstance(request, Request, {
4366
moduleName: 'workbox-strategies',
4467
className: this.constructor.name,
45-
funcName: 'handle',
68+
funcName: '_handle',
4669
paramName: 'request',
4770
});
4871
}
4972

50-
let error;
51-
let response;
73+
let error: Error | undefined = undefined;
74+
let response: Response | undefined;
75+
5276
try {
53-
response = await handler.fetch(request);
77+
const promises: Promise<Response|undefined>[] = [handler.fetch(request)];
78+
79+
if (this._networkTimeoutSeconds) {
80+
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000) as Promise<undefined>;
81+
promises.push(timeoutPromise);
82+
}
83+
84+
response = await Promise.race(promises);
85+
if (!response) {
86+
throw new Error(`Timed out the network response after ` +
87+
`${this._networkTimeoutSeconds} seconds.`);
88+
}
5489
} catch (err) {
5590
error = err;
5691
}
@@ -74,4 +109,4 @@ class NetworkOnly extends Strategy {
74109
}
75110
}
76111

77-
export {NetworkOnly};
112+
export {NetworkOnly, NetworkOnlyOptions};

‎test/workbox-strategies/sw/test-NetworkOnly.mjs

+29
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {cacheNames} from 'workbox-core/_private/cacheNames.mjs';
1010
import {NetworkOnly} from 'workbox-strategies/NetworkOnly.mjs';
1111
import {spyOnEvent} from '../../../infra/testing/helpers/extendable-event-utils.mjs';
1212
import {generateUniqueResponse} from '../../../infra/testing/helpers/generateUniqueResponse.mjs';
13+
import {sleep} from '../../../infra/testing/helpers/sleep.mjs';
1314

1415

1516
describe(`NetworkOnly`, function() {
@@ -138,5 +139,33 @@ describe(`NetworkOnly`, function() {
138139
expect(fetchStub.calledOnce).to.be.true;
139140
expect(fetchStub.calledWith(request, fetchOptions)).to.be.true;
140141
});
142+
143+
it(`should throw if the network request times out`, async function() {
144+
const request = new Request('http://example.io/test/');
145+
const event = new FetchEvent('fetch', {request});
146+
spyOnEvent(event);
147+
148+
// Use a short timeout to not slow down the test.
149+
// Note Sinon fake timers do not work with `await timeout()` used
150+
// in the current `StrategyHandler` implementation.
151+
const networkTimeoutSeconds = 0.5;
152+
const sleepLongerThanNetworkTimeout =
153+
sleep(2 * networkTimeoutSeconds * 1000);
154+
155+
sandbox.stub(self, 'fetch').callsFake(async () => {
156+
await sleepLongerThanNetworkTimeout;
157+
return new Response('Unexpected Response');
158+
});
159+
160+
const networkOnly = new NetworkOnly({networkTimeoutSeconds});
161+
162+
await expectError(
163+
() => networkOnly.handle({event, request}),
164+
'no-response',
165+
(err) => {
166+
expect(err.details.error.message).to.include(networkTimeoutSeconds);
167+
},
168+
);
169+
});
141170
});
142171
});

0 commit comments

Comments
 (0)