diff --git a/packages/cookie_store/lib/cookie_store.dart b/packages/cookie_store/lib/cookie_store.dart index 7147f852820..0b5e16798a1 100644 --- a/packages/cookie_store/lib/cookie_store.dart +++ b/packages/cookie_store/lib/cookie_store.dart @@ -6,12 +6,8 @@ /// This package has an implementation independent api and provides utilities /// that make it easy to extend and implemented into existing storage backends /// like SQLite databases. -/// -/// Users of the [cookie_jar](https://pub.dev/packages/cookie_jar) package -/// can use the provided `CookieJarAdapter`. library; -export 'src/cookie_jar_adapter.dart'; export 'src/cookie_persistence.dart'; export 'src/cookie_store.dart'; export 'src/storable_cookie.dart'; diff --git a/packages/cookie_store/lib/src/cookie_jar_adapter.dart b/packages/cookie_store/lib/src/cookie_jar_adapter.dart deleted file mode 100644 index 9be41b31e11..00000000000 --- a/packages/cookie_store/lib/src/cookie_jar_adapter.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:cookie_jar/cookie_jar.dart' as cookie_jar; -import 'package:cookie_store/src/cookie_store.dart'; -import 'package:cookie_store/src/utils.dart'; - -/// An adapter between the neon [CookieStore] and the [cookie_jar.CookieJar] -/// from the [cookie_jar](https://pub.dev/packages/cookie_jar) package. -final class CookieJarAdapter implements cookie_jar.CookieJar { - /// Creates a new cookie jar adapter backed by the given [cookieStore]. - const CookieJarAdapter(this.cookieStore); - - /// The neon cookie store backing the cookie jar. - final CookieStore cookieStore; - - @override - bool get ignoreExpires => false; - - @override - Future delete(Uri uri, [bool withDomainSharedCookie = false]) async { - return cookieStore.deleteWhere((cookie) { - return isDomainMatch(uri.host, cookie.domain ?? '') && isPathMatch(uri.path, cookie.path ?? ''); - }); - } - - @override - Future deleteAll() async { - return cookieStore.deleteAll(); - } - - @override - Future> loadForRequest(Uri uri) async => cookieStore.loadForRequest(uri); - - @override - Future saveFromResponse(Uri uri, List cookies) async => - cookieStore.saveFromResponse(uri, cookies); -} diff --git a/packages/cookie_store/pubspec.yaml b/packages/cookie_store/pubspec.yaml index 8255e8fb51a..2c702719880 100644 --- a/packages/cookie_store/pubspec.yaml +++ b/packages/cookie_store/pubspec.yaml @@ -7,7 +7,6 @@ environment: sdk: ^3.0.0 dependencies: - cookie_jar: ^4.0.0 meta: ^1.0.0 timezone: ^0.9.4 universal_io: ^2.0.0 diff --git a/packages/neon_framework/lib/src/models/account.dart b/packages/neon_framework/lib/src/models/account.dart index a45626f4de7..c42822be2e0 100644 --- a/packages/neon_framework/lib/src/models/account.dart +++ b/packages/neon_framework/lib/src/models/account.dart @@ -3,10 +3,10 @@ import 'dart:convert'; import 'package:built_value/built_value.dart'; import 'package:built_value/serializer.dart'; import 'package:built_value/standard_json_plugin.dart'; -import 'package:cookie_store/cookie_store.dart'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart'; import 'package:meta/meta.dart'; +import 'package:neon_framework/src/utils/cookie_store_interceptor.dart'; import 'package:neon_framework/src/utils/findable.dart'; import 'package:neon_framework/storage.dart'; import 'package:nextcloud/nextcloud.dart'; @@ -80,8 +80,14 @@ abstract class Account implements Credentials, Findable, Built + extends CookieInterceptor { + /// Creates a new interceptor persisting cookies. + CookieStoreInterceptor({ + required this.cookieStore, + }); + + /// The optional cookie jar to persist the response cookies. + final CookieStore cookieStore; + + @override + FutureOr> loadForRequest(Uri uri) { + return cookieStore.loadForRequest(uri); + } + + @override + FutureOr saveFromResponse(Uri uri, List cookies) { + return cookieStore.saveFromResponse(uri, cookies); + } +} diff --git a/packages/neon_framework/pubspec.yaml b/packages/neon_framework/pubspec.yaml index 45a895e1071..a5b6c3cbcc3 100644 --- a/packages/neon_framework/pubspec.yaml +++ b/packages/neon_framework/pubspec.yaml @@ -10,7 +10,6 @@ dependencies: built_collection: ^5.0.0 built_value: ^8.9.0 collection: ^1.0.0 - cookie_jar: ^4.0.0 cookie_store: git: url: https://github.com/nextcloud/neon diff --git a/packages/nextcloud/lib/src/client.dart b/packages/nextcloud/lib/src/client.dart index e2a964525c6..67b41143c77 100644 --- a/packages/nextcloud/lib/src/client.dart +++ b/packages/nextcloud/lib/src/client.dart @@ -1,9 +1,15 @@ +import 'package:built_collection/built_collection.dart'; import 'package:cookie_jar/cookie_jar.dart' as cookie_jar; import 'package:dynamite_runtime/http_client.dart'; import 'package:http/http.dart' as http; -import 'package:nextcloud/src/utils/cookie_jar_client.dart'; +import 'package:nextcloud/src/interceptors/base_header_interceptor.dart'; +import 'package:nextcloud/src/interceptors/cookie_interceptor.dart'; +import 'package:nextcloud/src/interceptors/http_interceptor.dart'; import 'package:universal_io/io.dart'; +/// A [HttpInterceptor] for the [NextcloudClient]; +typedef NextCloudInterceptor = HttpInterceptor; + /// A client configuring the clients for all Nextcloud APIs. /// /// To access the APIs of a particular app import the extensions through `package:nextcloud/{id}.dart`. @@ -26,16 +32,32 @@ class NextcloudClient extends DynamiteClient with http.BaseClient { String? password, String? appPassword, String? userAgent, - http.Client? httpClient, - cookie_jar.CookieJar? cookieJar, - }) : super( - httpClient: CookieJarClient( - httpClient: httpClient, - cookieJar: cookieJar, - baseHeaders: { - if (userAgent != null) HttpHeaders.userAgentHeader: userAgent, - }, - ), + super.httpClient, + @Deprecated('Use the CookieJarInterceptor instead') cookie_jar.CookieJar? cookieJar, + Iterable? interceptors, + }) : _interceptors = BuiltList.build((builder) { + if (interceptors != null) { + builder.addAll(interceptors); + } + + if (cookieJar != null) { + builder.add( + CookieJarInterceptor(cookieJar: cookieJar), + ); + } + + // Adding last to not overwrite request headers to avoid invalid requests. + if (userAgent != null) { + builder.add( + BaseHeaderInterceptor( + baseHeaders: { + HttpHeaders.userAgentHeader: userAgent, + }, + ), + ); + } + }), + super( authentications: [ if (appPassword != null) DynamiteHttpBearerAuthentication( @@ -49,6 +71,36 @@ class NextcloudClient extends DynamiteClient with http.BaseClient { ], ); + final BuiltList _interceptors; + @override - Future send(http.BaseRequest request) => httpClient.send(request); + Future send(http.BaseRequest request) { + if (_interceptors.isNotEmpty) { + return _sendIntercepted(request); + } + + return httpClient.send(request); + } + + Future _sendIntercepted(http.BaseRequest request) async { + var interceptedRequest = request; + for (final interceptor in _interceptors) { + if (interceptor.shouldInterceptRequest()) { + interceptedRequest = await interceptor.interceptRequest( + request: interceptedRequest, + ); + } + } + + var interceptedResponse = await httpClient.send(interceptedRequest); + for (final interceptor in _interceptors) { + if (interceptor.shouldInterceptResponse()) { + interceptedResponse = await interceptor.interceptResponse( + response: interceptedResponse, + ); + } + } + + return interceptedResponse; + } } diff --git a/packages/nextcloud/lib/src/interceptors/base_header_interceptor.dart b/packages/nextcloud/lib/src/interceptors/base_header_interceptor.dart new file mode 100644 index 00000000000..96d00a317a1 --- /dev/null +++ b/packages/nextcloud/lib/src/interceptors/base_header_interceptor.dart @@ -0,0 +1,37 @@ +import 'package:http/http.dart' as http; +import 'package:meta/meta.dart'; +import 'package:nextcloud/src/interceptors/http_interceptor.dart'; + +/// A HttpInterceptor that adds the given [baseHeaders] to a request. +/// +/// The headers of a request will override any header in the `baseHeaders`. +@internal +final class BaseHeaderInterceptor implements HttpInterceptor { + /// Creates a new base header interceptor. + const BaseHeaderInterceptor({ + this.baseHeaders, + }); + + /// The base headers added to each request. + final Map? baseHeaders; + + @override + bool shouldInterceptRequest() => true; + + @override + S interceptRequest({required S request}) { + baseHeaders?.forEach((key, value) { + request.headers.putIfAbsent(key, () => value); + }); + + return request; + } + + @override + bool shouldInterceptResponse() => false; + + @override + Never interceptResponse({required http.BaseResponse response}) { + throw UnsupportedError('Base headers can not be added to responses.'); + } +} diff --git a/packages/nextcloud/lib/src/interceptors/cookie_interceptor.dart b/packages/nextcloud/lib/src/interceptors/cookie_interceptor.dart new file mode 100644 index 00000000000..75b64722ec4 --- /dev/null +++ b/packages/nextcloud/lib/src/interceptors/cookie_interceptor.dart @@ -0,0 +1,90 @@ +import 'dart:async'; + +import 'package:cookie_jar/cookie_jar.dart' as cookie_jar; +import 'package:http/http.dart' as http; +import 'package:meta/meta.dart'; +import 'package:nextcloud/src/interceptors/http_interceptor.dart'; +import 'package:universal_io/io.dart'; + +/// A HttpInterceptor to implement cookie persisting interceptors. +abstract class CookieInterceptor + implements HttpInterceptor { + @override + bool shouldInterceptRequest() => true; + + @override + Future interceptRequest({required S request}) async { + final cookies = await loadForRequest(request.url); + if (cookies.isNotEmpty) { + final buffer = StringBuffer(); + final iterator = cookies.iterator..moveNext(); + + while (true) { + final cookie = iterator.current; + + buffer + ..write(cookie.name) + ..write('=') + ..write(cookie.value); + + if (iterator.moveNext()) { + buffer.write('; '); + } else { + break; + } + } + + request.headers['cookie'] = buffer.toString(); + } + + return request; + } + + @override + bool shouldInterceptResponse() => true; + + @override + Future interceptResponse({required T response}) async { + final cookieHeader = response.headersSplitValues['Set-Cookie']; + if (cookieHeader != null) { + final url = response.request?.url; + if (url == null) { + throw http.ClientException('Response does not contain any url. Cookies are ignored.'); + } + + final cookies = cookieHeader.map(Cookie.fromSetCookieValue).toList(); + await saveFromResponse(url, cookies); + } + + return response; + } + + /// Load the cookies for specified [uri]. + FutureOr> loadForRequest(Uri uri); + + /// Save the [cookies] for specified [uri]. + FutureOr saveFromResponse(Uri uri, List cookies); +} + +/// A HttpInterceptor persisting cookies in the provided [cookieJar]. +@internal +final class CookieJarInterceptor + extends CookieInterceptor { + /// Creates a new interceptor persisting cookies. + CookieJarInterceptor({ + required this.cookieJar, + }); + + /// The optional cookie jar to persist the response cookies. + final cookie_jar.CookieJar cookieJar; + + @override + Future> loadForRequest(Uri uri) { + return cookieJar.loadForRequest(uri); + } + + @override + Future saveFromResponse(Uri uri, List cookies) { + return cookieJar.saveFromResponse(uri, cookies); + } +} diff --git a/packages/nextcloud/lib/src/interceptors/http_interceptor.dart b/packages/nextcloud/lib/src/interceptors/http_interceptor.dart new file mode 100644 index 00000000000..5950df3fd7c --- /dev/null +++ b/packages/nextcloud/lib/src/interceptors/http_interceptor.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:http/http.dart' as http; +import 'package:meta/meta.dart'; + +/// Interceptor that can manipulate http requests and responses. +@immutable +abstract interface class HttpInterceptor { + /// Whether this interceptor should intercept requests. + bool shouldInterceptRequest(); + + /// Intercepts the given [request]. + /// + /// Provided requests are not finalized yet. It is an error for an interceptor + /// to request itself. + FutureOr interceptRequest({required S request}); + + /// Whether this interceptor should intercept response. + bool shouldInterceptResponse(); + + /// Intercepts the given [response]. + FutureOr interceptResponse({required T response}); +} diff --git a/packages/nextcloud/lib/src/utils/cookie_jar_client.dart b/packages/nextcloud/lib/src/utils/cookie_jar_client.dart deleted file mode 100644 index b4079c2d063..00000000000 --- a/packages/nextcloud/lib/src/utils/cookie_jar_client.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:cookie_jar/cookie_jar.dart' as cookie_jar; -import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; -import 'package:universal_io/io.dart'; - -/// An [http.Client] that persists cookies in the given [cookieJar]. -/// -/// The [baseHeaders] will be attached to every request. -/// The headers of a request will override any header in the `baseHeaders`. -@internal -class CookieJarClient with http.BaseClient { - /// Creates a new http client with a [cookieJar] persisting cookies. - CookieJarClient({ - this.baseHeaders, - this.cookieJar, - http.Client? httpClient, - }) : httpClient = httpClient ?? http.Client(); - - /// The base http client. - final http.Client httpClient; - - /// The optional cookie jar to persist the response cookies. - final cookie_jar.CookieJar? cookieJar; - - /// The base headers added to each request. - final Map? baseHeaders; - - /// Sends an HTTP request and asynchronously returns the response. - /// - /// Cookies are persisted in the [cookieJar] and loaded for requests. - @override - Future send(http.BaseRequest request) async { - if (cookieJar != null) { - final cookies = await cookieJar!.loadForRequest(request.url); - if (cookies.isNotEmpty) { - final buffer = StringBuffer(); - - for (final entry in cookies.indexed) { - final cookie = entry.$2; - - buffer - ..write(cookie.name) - ..write('=') - ..write(cookie.value); - - if (entry.$1 < cookies.length - 1) { - buffer.write('; '); - } - } - - request.headers['cookie'] = buffer.toString(); - } - } - - // Do not overwrite request headers to avoid invalid requests. - baseHeaders?.forEach((key, value) { - request.headers.putIfAbsent(key, () => value); - }); - - final response = await httpClient.send(request); - - final cookieHeader = response.headersSplitValues['set-cookie']; - if (cookieHeader != null && cookieJar != null) { - final cookies = cookieHeader.map(Cookie.fromSetCookieValue).toList(); - await cookieJar!.saveFromResponse(request.url, cookies); - } - - return response; - } -} diff --git a/packages/nextcloud/lib/utils.dart b/packages/nextcloud/lib/utils.dart index 0779a6236ae..832d468c160 100644 --- a/packages/nextcloud/lib/utils.dart +++ b/packages/nextcloud/lib/utils.dart @@ -1,5 +1,7 @@ /// This library contains various utility methods for working with nextcloud APIs. library; +export 'src/interceptors/cookie_interceptor.dart' show CookieInterceptor; +export 'src/interceptors/http_interceptor.dart'; export 'src/utils/date_time.dart' show DateTimeUtils; export 'src/utils/http_date_parser.dart'; diff --git a/packages/nextcloud/test/client_test.dart b/packages/nextcloud/test/client_test.dart index eb0a71c4f63..1633c227d95 100644 --- a/packages/nextcloud/test/client_test.dart +++ b/packages/nextcloud/test/client_test.dart @@ -1,12 +1,80 @@ +// ignore_for_file: discarded_futures + import 'package:cookie_jar/cookie_jar.dart'; import 'package:http/http.dart'; import 'package:http/testing.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:nextcloud/src/client.dart'; +import 'package:nextcloud/utils.dart'; import 'package:test/test.dart'; +class _MockInterceptor extends Mock implements HttpInterceptor {} + void main() { final uri = Uri.parse('http://example.com'); group(NextcloudClient, () { + group('interceptors', () { + final fakeRequest = Request('PUT', uri); + final fakeResponse = StreamedResponse(const Stream.empty(), 200); + + final mockedClient = MockClient((request) async { + return Response.fromStream(fakeResponse); + }); + late NextCloudInterceptor interceptor; + late NextcloudClient client; + + setUpAll(() { + registerFallbackValue(fakeResponse); + registerFallbackValue(fakeRequest); + }); + + setUp(() { + interceptor = _MockInterceptor(); + + client = NextcloudClient( + uri, + httpClient: mockedClient, + interceptors: [interceptor], + ); + }); + + test('does not intercept', () async { + when(() => interceptor.shouldInterceptRequest()).thenReturn(false); + when(() => interceptor.shouldInterceptResponse()).thenReturn(false); + + final request = Request('GET', uri); + await client.send(request); + + verifyNever(() => interceptor.interceptRequest(request: any(named: 'request', that: equals(request)))); + verifyNever(() => interceptor.interceptResponse(response: any(named: 'response', that: equals(fakeResponse)))); + }); + + test('does intercept', () async { + when(() => interceptor.shouldInterceptRequest()).thenReturn(true); + when(() => interceptor.shouldInterceptResponse()).thenReturn(true); + when( + () => interceptor.interceptRequest(request: any(named: 'request')), + ).thenReturn(fakeRequest); + when( + () => interceptor.interceptResponse(response: any(named: 'response')), + ).thenReturn(fakeResponse); + + final request = Request('GET', uri); + await client.send(request); + + verify( + () => interceptor.interceptRequest( + request: any(named: 'request', that: equals(request)), + ), + ).called(1); + verify( + () => interceptor.interceptResponse( + response: any(named: 'response'), + ), + ).called(1); + }); + }); + group('Cookies', () { late CookieJar cookieJar; setUp(() { @@ -29,6 +97,7 @@ void main() { final client = NextcloudClient( uri, httpClient: mockedClient, + // ignore: deprecated_member_use_from_same_package cookieJar: cookieJar, ); @@ -56,6 +125,7 @@ void main() { final client = NextcloudClient( uri, httpClient: mockedClient, + // ignore: deprecated_member_use_from_same_package cookieJar: cookieJar, ); diff --git a/packages/nextcloud/test/interceptors/base_header_interceptor_test.dart b/packages/nextcloud/test/interceptors/base_header_interceptor_test.dart new file mode 100644 index 00000000000..d2fcbbac5cf --- /dev/null +++ b/packages/nextcloud/test/interceptors/base_header_interceptor_test.dart @@ -0,0 +1,35 @@ +import 'package:http/http.dart' as http; +import 'package:nextcloud/src/interceptors/base_header_interceptor.dart'; +import 'package:test/test.dart'; + +void main() { + group('BaseHeaderInterceptor', () { + test('adds headers to intercepted request ', () { + const interceptor = BaseHeaderInterceptor(baseHeaders: {'KEY': 'VALUE'}); + + final request = http.Request('GET', Uri()); + + expect(interceptor.shouldInterceptRequest(), isTrue); + expect(interceptor.interceptRequest(request: request).headers, equals({'KEY': 'VALUE'})); + }); + + test('does not override existing headers', () { + const interceptor = BaseHeaderInterceptor(baseHeaders: {'KEY': 'VALUE'}); + + final request = http.Request('GET', Uri())..headers['KEY'] = 'NO-VALUE'; + + expect(interceptor.shouldInterceptRequest(), isTrue); + expect(interceptor.interceptRequest(request: request).headers, equals({'KEY': 'NO-VALUE'})); + }); + + test('does not intercept response', () { + const interceptor = BaseHeaderInterceptor(); + + expect(interceptor.shouldInterceptResponse(), isFalse); + expect( + () => interceptor.interceptResponse(response: http.Response('', 200)), + throwsUnsupportedError, + ); + }); + }); +} diff --git a/packages/nextcloud/test/interceptors/cookie_interceptor_test.dart b/packages/nextcloud/test/interceptors/cookie_interceptor_test.dart new file mode 100644 index 00000000000..42716b8af33 --- /dev/null +++ b/packages/nextcloud/test/interceptors/cookie_interceptor_test.dart @@ -0,0 +1,82 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cookie_jar/cookie_jar.dart' as cookie_jar; +import 'package:http/http.dart' as http; +import 'package:mocktail/mocktail.dart'; +import 'package:nextcloud/src/interceptors/cookie_interceptor.dart'; +import 'package:test/test.dart'; +import 'package:universal_io/io.dart'; + +class _MockCookieJar extends Mock implements cookie_jar.CookieJar {} + +void main() { + group('CookieInterceptor', () { + late cookie_jar.CookieJar cookieJar; + + setUpAll(() { + registerFallbackValue(http.Request('PUT', Uri())); + registerFallbackValue(http.StreamedResponse(const Stream.empty(), 200)); + registerFallbackValue(Uri()); + }); + + setUp(() { + cookieJar = _MockCookieJar(); + }); + + test('does add cookies to request', () async { + final interceptor = CookieJarInterceptor(cookieJar: cookieJar); + when(() => cookieJar.loadForRequest(any())).thenAnswer((_) async { + return [ + Cookie('a', 'a2'), + Cookie('b', 'b2'), + Cookie('c', 'c2'), + ]; + }); + + final request = http.Request('GET', Uri(host: 'host')); + final intercepted = await interceptor.interceptRequest(request: request); + + expect(interceptor.shouldInterceptRequest(), isTrue); + expect(intercepted.headers, equals({'cookie': 'a=a2; b=b2; c=c2'})); + + verify( + () => cookieJar.loadForRequest(any(that: equals(Uri(host: 'host')))), + ).called(1); + }); + + test('does store response cookies', () async { + final interceptor = CookieJarInterceptor(cookieJar: cookieJar); + when(() => cookieJar.saveFromResponse(any(), any())).thenAnswer((_) async {}); + + final response = http.Response( + '', + 200, + request: http.Request('GET', Uri(host: 'host')), + headers: {'Set-Cookie': 'a=a2,b=b2,c=c2'}, + ); + final intercepted = await interceptor.interceptResponse(response: response); + + expect(interceptor.shouldInterceptResponse(), isTrue); + expect(intercepted.headers, equals({'Set-Cookie': 'a=a2,b=b2,c=c2'})); + + verify( + () => cookieJar.saveFromResponse( + any(that: equals(Uri(host: 'host'))), + any(that: hasLength(3)), + ), + ).called(1); + }); + + test('throws when response has a null request', () async { + final interceptor = CookieJarInterceptor(cookieJar: cookieJar); + + final response = http.Response( + '', + 200, + headers: {'Set-Cookie': 'a=a2,b=b2,c=c2'}, + ); + expect(() => interceptor.interceptResponse(response: response), throwsException); + }); + }); +} diff --git a/packages/nextcloud/test/interceptors/http_interceptor_test.dart b/packages/nextcloud/test/interceptors/http_interceptor_test.dart new file mode 100644 index 00000000000..f7956dfd074 --- /dev/null +++ b/packages/nextcloud/test/interceptors/http_interceptor_test.dart @@ -0,0 +1,25 @@ +import 'package:http/http.dart' as http; +import 'package:nextcloud/utils.dart'; +import 'package:test/test.dart'; + +class _TestHttpInterceptor implements HttpInterceptor { + @override + http.BaseRequest interceptRequest({required http.BaseRequest request}) => throw UnimplementedError(); + + @override + http.BaseResponse interceptResponse({required http.BaseResponse response}) => throw UnimplementedError(); + + @override + bool shouldInterceptRequest() => throw UnimplementedError(); + + @override + bool shouldInterceptResponse() => throw UnimplementedError(); +} + +void main() { + group('HttpInterceptor', () { + test('can be implemented', () { + expect(_TestHttpInterceptor(), isNotNull); + }); + }); +} diff --git a/packages/nextcloud_test/lib/src/cookie_store_interceptor.dart b/packages/nextcloud_test/lib/src/cookie_store_interceptor.dart new file mode 100644 index 00000000000..b5e070245d6 --- /dev/null +++ b/packages/nextcloud_test/lib/src/cookie_store_interceptor.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:cookie_store/cookie_store.dart'; +import 'package:http/http.dart' as http; +import 'package:meta/meta.dart'; +import 'package:nextcloud/utils.dart'; +import 'package:universal_io/io.dart'; + +/// A HttpInterceptor persisting cookies in the provided [cookieStore]. +@internal +final class CookieStoreInterceptor + extends CookieInterceptor { + /// Creates a new interceptor persisting cookies. + CookieStoreInterceptor({ + required this.cookieStore, + }); + + /// The optional cookie jar to persist the response cookies. + final CookieStore cookieStore; + + @override + FutureOr> loadForRequest(Uri uri) { + return cookieStore.loadForRequest(uri); + } + + @override + FutureOr saveFromResponse(Uri uri, List cookies) { + return cookieStore.saveFromResponse(uri, cookies); + } +} diff --git a/packages/nextcloud_test/lib/src/test_client.dart b/packages/nextcloud_test/lib/src/test_client.dart index dfe171b4992..ef239adf665 100644 --- a/packages/nextcloud_test/lib/src/test_client.dart +++ b/packages/nextcloud_test/lib/src/test_client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:cookie_store/cookie_store.dart'; import 'package:nextcloud/nextcloud.dart'; +import 'package:nextcloud_test/src/cookie_store_interceptor.dart'; import 'package:nextcloud_test/src/docker_container.dart'; import 'package:nextcloud_test/src/fixtures.dart'; import 'package:nextcloud_test/src/proxy_http_client.dart'; @@ -54,9 +55,11 @@ extension TestNextcloudClient on NextcloudClient { loginName: username, password: username, appPassword: appPassword, - cookieJar: CookieJarAdapter( - CookieStore(), - ), + interceptors: [ + CookieStoreInterceptor( + cookieStore: CookieStore(), + ), + ], httpClient: getProxyHttpClient( onRequest: appendFixture, ),