diff --git a/pkgs/http/lib/browser_client.dart b/pkgs/http/lib/browser_client.dart index 2cd0e5c7a4..931c8b9828 100644 --- a/pkgs/http/lib/browser_client.dart +++ b/pkgs/http/lib/browser_client.dart @@ -2,4 +2,4 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -export 'src/browser_client.dart' show BrowserClient; +export 'src/browser_client.dart' show BrowserClient, CacheMode, RequestMode; diff --git a/pkgs/http/lib/src/browser_client.dart b/pkgs/http/lib/src/browser_client.dart index acf233448a..93a7a3ee0d 100644 --- a/pkgs/http/lib/src/browser_client.dart +++ b/pkgs/http/lib/src/browser_client.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:js_interop'; - import 'package:web/web.dart' show AbortController, @@ -30,6 +29,38 @@ BaseClient createClient() { return BrowserClient(); } +/// Caching mode used by the [BrowserClient]. +/// +/// Sets the request cache value of the browser Fetch API. +/// [`Request.cache`](https://developer.mozilla.org/en-US/docs/Web/API/Request/cache) property. +enum CacheMode { + defaultType('default'), + reload('reload'), + noStore('no-store'), + noCache('no-cache'), + forceCache('force-cache'), + onlyIfCached('only-if-cached'); + + final String cacheType; + + const CacheMode(this.cacheType); +} + +/// Request mode used by the [BrowserClient]. +/// +/// Sets the request mode value of the browser Fetch API. +/// [`Request.mode`](https://developer.mozilla.org/en-US/docs/Web/API/Request/mode) property. +enum RequestMode { + sameOrigin('same-origin'), + noCors('no-cors'), + cors('cors'), + navigate('navigate'); + + final String requestType; + + const RequestMode(this.requestType); +} + @JS('fetch') external JSPromise _fetch( RequestInfo input, [ @@ -51,6 +82,23 @@ external JSPromise _fetch( class BrowserClient extends BaseClient { final _abortController = AbortController(); + final CacheMode _cacheMode; + + final RequestMode _requestMode; + /// Create a new browser-based HTTP Client. + /// + /// If [cacheMode] is provided then it can be used to cache the request + /// in the browser. + /// + /// For example: + /// ```dart + /// final client = BrowserClient(cacheMode: CacheMode.reload); + /// ``` + BrowserClient({RequestMode requestMode = RequestMode.cors, + CacheMode cacheMode = CacheMode.defaultType}) + : _cacheMode = cacheMode, + _requestMode = requestMode; + /// Whether to send credentials such as cookies or authorization headers for /// cross-site requests. /// @@ -74,6 +122,8 @@ class BrowserClient extends BaseClient { RequestInit( method: request.method, body: bodyBytes.isNotEmpty ? bodyBytes.toJS : null, + cache: _cacheMode.cacheType, + mode: _requestMode.requestType, credentials: withCredentials ? 'include' : 'same-origin', headers: { if (request.contentLength case final contentLength?) diff --git a/pkgs/http/test/html/cache_test.dart b/pkgs/http/test/html/cache_test.dart new file mode 100644 index 0000000000..6a97086877 --- /dev/null +++ b/pkgs/http/test/html/cache_test.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('browser') +library; + +import 'dart:async'; + +import 'package:http/browser_client.dart'; +import 'package:http/http.dart' as http; +import 'package:http/src/exception.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + test('#send a StreamedRequest with default type', () async { + var client = BrowserClient(cacheMode: CacheMode.defaultType); + var request = http.StreamedRequest('POST', echoUrl); + var responseFuture = client.send(request); + request.sink.add('{"hello": "world"}'.codeUnits); + unawaited(request.sink.close()); + + var response = await responseFuture; + + client.close(); + + expect(response.statusCode, 200); + expect(response.reasonPhrase, 'OK'); + }, skip: 'Need to fix server tests for browser'); + + test('#send a StreamedRequest with reload type', () async { + var client = BrowserClient(cacheMode: CacheMode.reload); + var request = http.StreamedRequest('POST', echoUrl); + + var responseFuture = client.send(request); + request.sink.add('{"hello": "world"}'.codeUnits); + unawaited(request.sink.close()); + var response = await responseFuture; + var bytesString = await response.stream.bytesToString(); + + client.close(); + + expect(bytesString, contains('no-cache')); + }, skip: 'Need to fix server tests for browser'); + + test('#send a StreamedRequest with no-cache type', () async { + var client = BrowserClient(cacheMode: CacheMode.noCache); + var request = http.StreamedRequest('POST', echoUrl); + + var responseFuture = client.send(request); + request.sink.add('{"hello": "world"}'.codeUnits); + unawaited(request.sink.close()); + var response = await responseFuture; + var bytesString = await response.stream.bytesToString(); + + client.close(); + expect(bytesString, contains('max-age=0')); + }, skip: 'Need to fix server tests for browser'); + + test('#send a StreamedRequest with only-if-cached type', () { + var client = BrowserClient(cacheMode: CacheMode.onlyIfCached); + var request = http.StreamedRequest('POST', echoUrl); + + expectLater(client.send(request), throwsA(isA())); + request.sink.add('{"hello": "world"}'.codeUnits); + unawaited(request.sink.close()); + + client.close(); + }, skip: 'Need to fix server tests for browser'); + + test('#send with an invalid URL', () { + var client = BrowserClient(cacheMode: CacheMode.onlyIfCached); + var url = Uri.http('http.invalid', ''); + var request = http.StreamedRequest('POST', url); + + expect(client.send(request), throwsClientException()); + + request.sink.add('{"hello": "world"}'.codeUnits); + request.sink.close(); + }, skip: 'Need to fix server tests for browser'); +}