From 15c7647b8d39c95c5a0ce51f26284d91044468ee Mon Sep 17 00:00:00 2001 From: Maurice Raguse Date: Tue, 2 Jun 2026 13:30:18 +0200 Subject: [PATCH 1/4] add enum for omit value --- pkgs/http/lib/src/browser_client.dart | 78 +++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/pkgs/http/lib/src/browser_client.dart b/pkgs/http/lib/src/browser_client.dart index e4e30a3886..4d07b05db1 100644 --- a/pkgs/http/lib/src/browser_client.dart +++ b/pkgs/http/lib/src/browser_client.dart @@ -35,9 +35,39 @@ BaseClient createClient() { @JS('fetch') external JSPromise _fetch( - RequestInfo input, [ - RequestInit init, -]); + RequestInfo input, [ + RequestInit init, + ]); + +/// The browser `fetch` credentials mode used by [BrowserClient]. +/// +/// Controls whether the browser sends credentials such as cookies, TLS client +/// certificates, or authorization headers with a request. +/// +/// See also: +/// - https://fetch.spec.whatwg.org/#requestcredentials +enum BrowserCredentialsMode { + /// Never send credentials with the request and never include credentials in + /// the response. + /// + /// This corresponds to the browser `fetch` credentials mode `omit`. + omit('omit'), + + /// Send credentials for same-origin requests only. + /// + /// This corresponds to the browser `fetch` credentials mode `same-origin`. + sameOrigin('same-origin'), + + /// Always send credentials, even for cross-origin requests. + /// + /// This corresponds to the browser `fetch` credentials mode `include`. + include('include'); + + const BrowserCredentialsMode(this._value); + + /// The value passed to the browser `fetch` `RequestInit.credentials` field. + final String _value; +} /// A `package:web`-based HTTP client that runs in the browser and is backed by /// [`window.fetch`](https://fetch.spec.whatwg.org/). @@ -52,11 +82,49 @@ external JSPromise _fetch( /// Responses are streamed but requests are not. A request will only be sent /// once all the data is available. class BrowserClient extends BaseClient { + /// Create a [BrowserClient]. + /// + /// By default, credentials are sent for same-origin requests only, which + /// matches the previous default behavior when [withCredentials] was `false`. + BrowserClient({ + this.credentialsMode = BrowserCredentialsMode.sameOrigin, + }); + + /// The browser `fetch` credentials mode used for requests. + /// + /// Defaults to [BrowserCredentialsMode.sameOrigin], which matches the + /// previous behavior when [withCredentials] was `false`. + BrowserCredentialsMode credentialsMode; + /// Whether to send credentials such as cookies or authorization headers for /// cross-site requests. /// /// Defaults to `false`. - bool withCredentials = false; + /// + /// This property is deprecated because it can only represent two of the three + /// browser `fetch` credentials modes (`same-origin` and `include`). Use + /// [credentialsMode] instead to also support [BrowserCredentialsMode.omit]. + /// + /// Reading this property returns `true` only when [credentialsMode] is + /// [BrowserCredentialsMode.include]. + @Deprecated('Use credentialsMode instead.') + bool get withCredentials => + credentialsMode == BrowserCredentialsMode.include; + + /// Whether to send credentials such as cookies or authorization headers for + /// cross-site requests. + /// + /// Setting this to `true` sets [credentialsMode] to + /// [BrowserCredentialsMode.include]. + /// + /// Setting this to `false` sets [credentialsMode] to + /// [BrowserCredentialsMode.sameOrigin]. + @Deprecated('Use credentialsMode instead.') + set withCredentials(bool value) { + credentialsMode = value + ? BrowserCredentialsMode.include + : BrowserCredentialsMode.sameOrigin; + } bool _isClosed = false; final _openRequestAbortControllers = []; @@ -85,7 +153,7 @@ class BrowserClient extends BaseClient { RequestInit( method: request.method, body: bodyBytes.isNotEmpty ? bodyBytes.toJS : null, - credentials: withCredentials ? 'include' : 'same-origin', + credentials: credentialsMode._value, headers: { if (request.contentLength case final contentLength?) 'content-length': contentLength, From cf337e4bf019797beafe3a7d6c2d9522079c367c Mon Sep 17 00:00:00 2001 From: Maurice Raguse Date: Tue, 2 Jun 2026 13:41:22 +0200 Subject: [PATCH 2/4] add changelog and new version --- pkgs/http/CHANGELOG.md | 4 ++++ pkgs/http/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/http/CHANGELOG.md b/pkgs/http/CHANGELOG.md index 67ced1f1d7..7fe61d6ba7 100644 --- a/pkgs/http/CHANGELOG.md +++ b/pkgs/http/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.2-wip + +* Add `BrowserCredentialsMode` and `BrowserClient.credentialsMode` to support the `omit` browser fetch credentials mode. Deprecate `withCredentials`. + ## 1.6.1-wip * Clarified the behavior of response headers in API documentation comments. diff --git a/pkgs/http/pubspec.yaml b/pkgs/http/pubspec.yaml index b140067b54..21a2107070 100644 --- a/pkgs/http/pubspec.yaml +++ b/pkgs/http/pubspec.yaml @@ -1,5 +1,5 @@ name: http -version: 1.6.1-wip +version: 1.6.2-wip description: A composable, multi-platform, Future-based API for HTTP requests. repository: https://github.com/dart-lang/http/tree/master/pkgs/http From 020f5876c6ac966973491c458a015ff41b681643 Mon Sep 17 00:00:00 2001 From: Maurice Raguse Date: Tue, 23 Jun 2026 09:31:59 +0200 Subject: [PATCH 3/4] Add requestCredentials parameter to BrowserClient to support full fetch credentials modes - Introduce `RequestCredentials` enum (`omit`, `same-origin`, `include`). - Deprecate `withCredentials` getter and setter while maintaining full backwards compatibility. - Update README.md with instructions on how to use the new parameter. - Add unit tests verifying correct constructor initialization and fallback behaviors. --- pkgs/http/README.md | 3 ++ pkgs/http/lib/src/browser_client.dart | 59 +++++++++++++------------ pkgs/http/test/browser_client_test.dart | 47 ++++++++++++++++++++ 3 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 pkgs/http/test/browser_client_test.dart diff --git a/pkgs/http/README.md b/pkgs/http/README.md index b3080c0551..d30556cec8 100644 --- a/pkgs/http/README.md +++ b/pkgs/http/README.md @@ -266,6 +266,9 @@ Some well-supported implementations are: | [`package:cronet_http`][cronethttp] — [`CronetClient`][cronetclient] | Android | Flutter | ✅︎ | ✅︎ | ― | | [`package:fetch_client`][fetch] — [`FetchClient`][fetchclient] | Web | Dart, Flutter | ✅︎ | ✅︎ | ✅︎ | +> [!NOTE] +> [`BrowserClient`][browserclient] allows you to configure the browser `fetch` credentials mode (e.g., `omit`, `same-origin`, `include`) by passing the `requestCredentials` parameter to its constructor. + > [!TIP] > If you are writing a Dart package or Flutter plugin that uses > `package:http`, you should not depend on a particular [`Client`][client] diff --git a/pkgs/http/lib/src/browser_client.dart b/pkgs/http/lib/src/browser_client.dart index 4d07b05db1..b65137deb7 100644 --- a/pkgs/http/lib/src/browser_client.dart +++ b/pkgs/http/lib/src/browser_client.dart @@ -35,35 +35,37 @@ BaseClient createClient() { @JS('fetch') external JSPromise _fetch( - RequestInfo input, [ - RequestInit init, - ]); + RequestInfo input, [ + RequestInit init, +]); -/// The browser `fetch` credentials mode used by [BrowserClient]. +/// The browser `fetch` credentials mode represented by [RequestCredentials]. /// /// Controls whether the browser sends credentials such as cookies, TLS client /// certificates, or authorization headers with a request. /// /// See also: /// - https://fetch.spec.whatwg.org/#requestcredentials -enum BrowserCredentialsMode { +enum RequestCredentials { /// Never send credentials with the request and never include credentials in /// the response. /// /// This corresponds to the browser `fetch` credentials mode `omit`. omit('omit'), - /// Send credentials for same-origin requests only. + /// Send credentials for same-origin requests only and only include + /// credentials in same-origin replies. /// /// This corresponds to the browser `fetch` credentials mode `same-origin`. sameOrigin('same-origin'), - /// Always send credentials, even for cross-origin requests. + /// Always send credentials, even for cross-origin requests, and include them + /// in all responses. /// /// This corresponds to the browser `fetch` credentials mode `include`. include('include'); - const BrowserCredentialsMode(this._value); + const RequestCredentials(this._value); /// The value passed to the browser `fetch` `RequestInit.credentials` field. final String _value; @@ -86,15 +88,18 @@ class BrowserClient extends BaseClient { /// /// By default, credentials are sent for same-origin requests only, which /// matches the previous default behavior when [withCredentials] was `false`. - BrowserClient({ - this.credentialsMode = BrowserCredentialsMode.sameOrigin, - }); + BrowserClient( + {RequestCredentials requestCredentials = RequestCredentials.sameOrigin}) + : _requestCredentials = requestCredentials; + + /// The internal browser `fetch` credentials mode used for requests. + RequestCredentials _requestCredentials; /// The browser `fetch` credentials mode used for requests. /// - /// Defaults to [BrowserCredentialsMode.sameOrigin], which matches the + /// Defaults to [RequestCredentials.sameOrigin], which matches the /// previous behavior when [withCredentials] was `false`. - BrowserCredentialsMode credentialsMode; + RequestCredentials get requestCredentials => _requestCredentials; /// Whether to send credentials such as cookies or authorization headers for /// cross-site requests. @@ -103,27 +108,25 @@ class BrowserClient extends BaseClient { /// /// This property is deprecated because it can only represent two of the three /// browser `fetch` credentials modes (`same-origin` and `include`). Use - /// [credentialsMode] instead to also support [BrowserCredentialsMode.omit]. + /// [_requestCredentials] instead to also support [RequestCredentials.omit]. /// - /// Reading this property returns `true` only when [credentialsMode] is - /// [BrowserCredentialsMode.include]. - @Deprecated('Use credentialsMode instead.') - bool get withCredentials => - credentialsMode == BrowserCredentialsMode.include; + /// Reading this property returns `true` only when [_requestCredentials] is + /// [RequestCredentials.include]. + @Deprecated('Use requestCredentials instead.') + bool get withCredentials => _requestCredentials == RequestCredentials.include; /// Whether to send credentials such as cookies or authorization headers for /// cross-site requests. /// - /// Setting this to `true` sets [credentialsMode] to - /// [BrowserCredentialsMode.include]. + /// Setting this to `true` sets [_requestCredentials] to + /// [RequestCredentials.include]. /// - /// Setting this to `false` sets [credentialsMode] to - /// [BrowserCredentialsMode.sameOrigin]. - @Deprecated('Use credentialsMode instead.') + /// Setting this to `false` sets [_requestCredentials] to + /// [RequestCredentials.sameOrigin]. + @Deprecated('Use the requestCredentials constructor parameter instead.') set withCredentials(bool value) { - credentialsMode = value - ? BrowserCredentialsMode.include - : BrowserCredentialsMode.sameOrigin; + _requestCredentials = + value ? RequestCredentials.include : RequestCredentials.sameOrigin; } bool _isClosed = false; @@ -153,7 +156,7 @@ class BrowserClient extends BaseClient { RequestInit( method: request.method, body: bodyBytes.isNotEmpty ? bodyBytes.toJS : null, - credentials: credentialsMode._value, + credentials: _requestCredentials._value, headers: { if (request.contentLength case final contentLength?) 'content-length': contentLength, diff --git a/pkgs/http/test/browser_client_test.dart b/pkgs/http/test/browser_client_test.dart new file mode 100644 index 0000000000..9476525b8d --- /dev/null +++ b/pkgs/http/test/browser_client_test.dart @@ -0,0 +1,47 @@ +// Copyright (c) 2026, 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. + +import 'package:http/src/browser_client.dart'; +import 'package:test/test.dart'; + +void main() { + group('RequestCredentials', () { + test('defaults to sameOrigin', () { + final client = BrowserClient(); + expect(client.requestCredentials, RequestCredentials.sameOrigin); + // ignore: deprecated_member_use_from_same_package + expect(client.withCredentials, isFalse); + }); + + test('can be set via constructor', () { + final client = + BrowserClient(requestCredentials: RequestCredentials.include); + expect(client.requestCredentials, RequestCredentials.include); + // ignore: deprecated_member_use_from_same_package + expect(client.withCredentials, isTrue); + + final clientOmit = + BrowserClient(requestCredentials: RequestCredentials.omit); + expect(clientOmit.requestCredentials, RequestCredentials.omit); + // ignore: deprecated_member_use_from_same_package + expect(clientOmit.withCredentials, isFalse); + }); + + test('deprecated withCredentials setter updates state correctly', () { + final client = BrowserClient() + + // ignore: deprecated_member_use_from_same_package + ..withCredentials = true; + expect(client.requestCredentials, RequestCredentials.include); + // ignore: deprecated_member_use_from_same_package + expect(client.withCredentials, isTrue); + + // ignore: deprecated_member_use_from_same_package + client.withCredentials = false; + expect(client.requestCredentials, RequestCredentials.sameOrigin); + // ignore: deprecated_member_use_from_same_package + expect(client.withCredentials, isFalse); + }); + }); +} From df5be035399ecfe511f3bae2c2d9ab476d5cc394 Mon Sep 17 00:00:00 2001 From: Maurice Raguse Date: Wed, 24 Jun 2026 14:54:02 +0200 Subject: [PATCH 4/4] - Reverted README.md changes; the feature is now documented exclusively in the class/constructor Dartdoc. - Cut down the constructor documentation to remove references to historical behavior. - Cleaned up the changelog entry by moving the note under the existing `1.6.1-wip` section and reverted the pubspec version bump. - Removed the unnecessary browser unit/integration tests. --- pkgs/http/CHANGELOG.md | 9 +++-- pkgs/http/README.md | 3 -- pkgs/http/lib/src/browser_client.dart | 3 +- pkgs/http/pubspec.yaml | 2 +- pkgs/http/test/browser_client_test.dart | 47 ------------------------- 5 files changed, 6 insertions(+), 58 deletions(-) delete mode 100644 pkgs/http/test/browser_client_test.dart diff --git a/pkgs/http/CHANGELOG.md b/pkgs/http/CHANGELOG.md index 7fe61d6ba7..33f3a03c94 100644 --- a/pkgs/http/CHANGELOG.md +++ b/pkgs/http/CHANGELOG.md @@ -1,9 +1,7 @@ -## 1.6.2-wip - -* Add `BrowserCredentialsMode` and `BrowserClient.credentialsMode` to support the `omit` browser fetch credentials mode. Deprecate `withCredentials`. - ## 1.6.1-wip +* Add `BrowserCredentialsMode` and `BrowserClient.credentialsMode` to support + the `omit` browser fetch credentials mode. Deprecate `withCredentials`. * Clarified the behavior of response headers in API documentation comments. * Make it more clear that `close` must be called for correctness. * Replace references to `dart:web` with `package:web` dartdoc. @@ -50,7 +48,8 @@ ## 1.2.0 * Add `MockClient.pngResponse`, which makes it easier to fake image responses. -* Added the ability to fetch the URL of the response through `BaseResponseWithUrl`. +* Added the ability to fetch the URL of the response through + `BaseResponseWithUrl`. * Add the ability to get headers as a `Map` to `BaseResponse`. diff --git a/pkgs/http/README.md b/pkgs/http/README.md index d30556cec8..b3080c0551 100644 --- a/pkgs/http/README.md +++ b/pkgs/http/README.md @@ -266,9 +266,6 @@ Some well-supported implementations are: | [`package:cronet_http`][cronethttp] — [`CronetClient`][cronetclient] | Android | Flutter | ✅︎ | ✅︎ | ― | | [`package:fetch_client`][fetch] — [`FetchClient`][fetchclient] | Web | Dart, Flutter | ✅︎ | ✅︎ | ✅︎ | -> [!NOTE] -> [`BrowserClient`][browserclient] allows you to configure the browser `fetch` credentials mode (e.g., `omit`, `same-origin`, `include`) by passing the `requestCredentials` parameter to its constructor. - > [!TIP] > If you are writing a Dart package or Flutter plugin that uses > `package:http`, you should not depend on a particular [`Client`][client] diff --git a/pkgs/http/lib/src/browser_client.dart b/pkgs/http/lib/src/browser_client.dart index b65137deb7..a9762c694d 100644 --- a/pkgs/http/lib/src/browser_client.dart +++ b/pkgs/http/lib/src/browser_client.dart @@ -86,8 +86,7 @@ enum RequestCredentials { class BrowserClient extends BaseClient { /// Create a [BrowserClient]. /// - /// By default, credentials are sent for same-origin requests only, which - /// matches the previous default behavior when [withCredentials] was `false`. + /// By default, credentials are sent for same-origin requests only. BrowserClient( {RequestCredentials requestCredentials = RequestCredentials.sameOrigin}) : _requestCredentials = requestCredentials; diff --git a/pkgs/http/pubspec.yaml b/pkgs/http/pubspec.yaml index 21a2107070..b140067b54 100644 --- a/pkgs/http/pubspec.yaml +++ b/pkgs/http/pubspec.yaml @@ -1,5 +1,5 @@ name: http -version: 1.6.2-wip +version: 1.6.1-wip description: A composable, multi-platform, Future-based API for HTTP requests. repository: https://github.com/dart-lang/http/tree/master/pkgs/http diff --git a/pkgs/http/test/browser_client_test.dart b/pkgs/http/test/browser_client_test.dart deleted file mode 100644 index 9476525b8d..0000000000 --- a/pkgs/http/test/browser_client_test.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2026, 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. - -import 'package:http/src/browser_client.dart'; -import 'package:test/test.dart'; - -void main() { - group('RequestCredentials', () { - test('defaults to sameOrigin', () { - final client = BrowserClient(); - expect(client.requestCredentials, RequestCredentials.sameOrigin); - // ignore: deprecated_member_use_from_same_package - expect(client.withCredentials, isFalse); - }); - - test('can be set via constructor', () { - final client = - BrowserClient(requestCredentials: RequestCredentials.include); - expect(client.requestCredentials, RequestCredentials.include); - // ignore: deprecated_member_use_from_same_package - expect(client.withCredentials, isTrue); - - final clientOmit = - BrowserClient(requestCredentials: RequestCredentials.omit); - expect(clientOmit.requestCredentials, RequestCredentials.omit); - // ignore: deprecated_member_use_from_same_package - expect(clientOmit.withCredentials, isFalse); - }); - - test('deprecated withCredentials setter updates state correctly', () { - final client = BrowserClient() - - // ignore: deprecated_member_use_from_same_package - ..withCredentials = true; - expect(client.requestCredentials, RequestCredentials.include); - // ignore: deprecated_member_use_from_same_package - expect(client.withCredentials, isTrue); - - // ignore: deprecated_member_use_from_same_package - client.withCredentials = false; - expect(client.requestCredentials, RequestCredentials.sameOrigin); - // ignore: deprecated_member_use_from_same_package - expect(client.withCredentials, isFalse); - }); - }); -}