diff --git a/flutter_cache_manager/README.md b/flutter_cache_manager/README.md index 3c5b77e4..e32f7c71 100644 --- a/flutter_cache_manager/README.md +++ b/flutter_cache_manager/README.md @@ -60,7 +60,7 @@ you call this method with other height/width parameters. When your files are stored on Firebase Storage you can use [flutter_cache_manager_firebase](https://pub.dev/packages/flutter_cache_manager_firebase). ## Customize -The cache manager is customizable by creating a new CacheManager. It is very important to not create more than 1 +The cache manager is customizable by creating a new CacheManager. It is very important to not create more than one CacheManager instance with the same key as these bite each other. In the example down here the manager is created as a Singleton, but you could also use for example Provider to Provide a CacheManager on the top level of your app. Below is an example with other settings for the maximum age of files, maximum number of objects @@ -85,6 +85,7 @@ class CustomCacheManager { - [How are the cache files stored?](#how-are-the-cache-files-stored) - [When are the cached files updated?](#when-are-the-cached-files-updated) - [When are cached files removed?](#when-are-cached-files-removed) +- [Why are cached files kept even though the server sends max-age=0?](#why-are-cached-files-kept-even-though-the-server-sends-max-age0) ### How are the cache files stored? @@ -113,6 +114,20 @@ The cache knows when files have been used latest. When cleaning the cache (which deletes files when there are too many, ordered by last use, and when files just haven't been used for longer than the stale period. +### Why are cached files kept even though the server sends max-age=0? + +The case when a web server responds with `Cache-Control: max-age=0` is kind of an edge case. +It could either mean, that the content expires really fast (then `Cache-Control: no-cache` might +be a better response) or it could be that the server has some misconfiguration in place. + +There where some confusions among users of this library because of the second case (see also +[this issue](https://github.com/Baseflow/flutter_cache_manager/issues/283) so as a default +behaviour this library ignores `max-age=0` and instead sets the validity of the downloaded file +to `Duration(days: 7)`. + +If you want to treat `max-age=0` the same as `no-cache` (or something less rigid like 30 seconds), then +set the `Config` parameter `durationOnMaxAgeZero` to `Duration(seconds: 0)` or `30` or whatever you +think is appropriate. ## Breaking changes in v2 - There is no longer a need to extend on BaseCacheManager, you can directly call the constructor. The BaseCacheManager diff --git a/flutter_cache_manager/lib/src/config/_config_io.dart b/flutter_cache_manager/lib/src/config/_config_io.dart index 67eb8cb3..5790dc0b 100644 --- a/flutter_cache_manager/lib/src/config/_config_io.dart +++ b/flutter_cache_manager/lib/src/config/_config_io.dart @@ -7,15 +7,21 @@ class Config implements def.Config { Config( this.cacheKey, { Duration? stalePeriod, + Duration? durationOnMaxAgeZero, int? maxNrOfCacheObjects, CacheInfoRepository? repo, FileSystem? fileSystem, FileService? fileService, }) : stalePeriod = stalePeriod ?? const Duration(days: 30), + durationOnMaxAgeZero = durationOnMaxAgeZero ?? const Duration(days: 7), maxNrOfCacheObjects = maxNrOfCacheObjects ?? 200, repo = repo ?? _createRepo(cacheKey), fileSystem = fileSystem ?? IOFileSystem(cacheKey), - fileService = fileService ?? HttpFileService(); + fileService = fileService ?? + HttpFileService( + durationOnMaxAgeZero: durationOnMaxAgeZero ?? + Duration(days: 7), + ); @override final CacheInfoRepository repo; @@ -29,6 +35,9 @@ class Config implements def.Config { @override final Duration stalePeriod; + @override + final Duration durationOnMaxAgeZero; + @override final int maxNrOfCacheObjects; diff --git a/flutter_cache_manager/lib/src/config/_config_unsupported.dart b/flutter_cache_manager/lib/src/config/_config_unsupported.dart index b14ebeb3..e6ef48ad 100644 --- a/flutter_cache_manager/lib/src/config/_config_unsupported.dart +++ b/flutter_cache_manager/lib/src/config/_config_unsupported.dart @@ -11,6 +11,8 @@ class Config implements def.Config { //ignore: avoid_unused_constructor_parameters Duration? stalePeriod, //ignore: avoid_unused_constructor_parameters + Duration? durationOnMaxAgeZero, + //ignore: avoid_unused_constructor_parameters int? maxNrOfCacheObjects, //ignore: avoid_unused_constructor_parameters CacheInfoRepository? repo, @@ -34,6 +36,9 @@ class Config implements def.Config { @override Duration get stalePeriod => throw UnimplementedError(); + @override + Duration get durationOnMaxAgeZero => throw UnimplementedError(); + @override int get maxNrOfCacheObjects => throw UnimplementedError(); diff --git a/flutter_cache_manager/lib/src/config/_config_web.dart b/flutter_cache_manager/lib/src/config/_config_web.dart index 99e4f3b8..da906d7c 100644 --- a/flutter_cache_manager/lib/src/config/_config_web.dart +++ b/flutter_cache_manager/lib/src/config/_config_web.dart @@ -8,15 +8,21 @@ class Config implements def.Config { Config( this.cacheKey, { Duration? stalePeriod, + Duration? durationOnMaxAgeZero, int? maxNrOfCacheObjects, CacheInfoRepository? repo, FileSystem? fileSystem, FileService? fileService, }) : stalePeriod = stalePeriod ?? const Duration(days: 30), + durationOnMaxAgeZero = durationOnMaxAgeZero ?? const Duration(days: 7), maxNrOfCacheObjects = maxNrOfCacheObjects ?? 200, repo = repo ?? NonStoringObjectProvider(), fileSystem = fileSystem ?? MemoryCacheSystem(), - fileService = fileService ?? HttpFileService(); + fileService = fileService ?? + HttpFileService( + durationOnMaxAgeZero: durationOnMaxAgeZero ?? + Duration(days: 7), + ); @override final CacheInfoRepository repo; @@ -30,6 +36,9 @@ class Config implements def.Config { @override final Duration stalePeriod; + @override + final Duration durationOnMaxAgeZero; + @override final int maxNrOfCacheObjects; diff --git a/flutter_cache_manager/lib/src/config/config.dart b/flutter_cache_manager/lib/src/config/config.dart index 1af4730f..b81b4cea 100644 --- a/flutter_cache_manager/lib/src/config/config.dart +++ b/flutter_cache_manager/lib/src/config/config.dart @@ -10,6 +10,8 @@ abstract class Config { /// [stalePeriod] is the time duration in which a cache object is /// considered 'stale'. When a file is cached but not being used for a /// certain time the file will be deleted. + /// [durationOnMaxAgeZero] is the time duration a fetched API response is + /// considered up-to-date if the server responds with "max-age=0" /// [maxNrOfCacheObjects] defines how large the cache is allowed to be. If /// there are more files the files that haven't been used for the longest /// time will be removed. @@ -23,6 +25,7 @@ abstract class Config { factory Config( String cacheKey, { Duration stalePeriod, + Duration durationOnMaxAgeZero, int maxNrOfCacheObjects, CacheInfoRepository repo, FileSystem fileSystem, @@ -33,6 +36,8 @@ abstract class Config { Duration get stalePeriod; + Duration get durationOnMaxAgeZero; + int get maxNrOfCacheObjects; CacheInfoRepository get repo; diff --git a/flutter_cache_manager/lib/src/web/file_service.dart b/flutter_cache_manager/lib/src/web/file_service.dart index 2dcd01d6..ed64cf06 100644 --- a/flutter_cache_manager/lib/src/web/file_service.dart +++ b/flutter_cache_manager/lib/src/web/file_service.dart @@ -23,9 +23,11 @@ abstract class FileService { /// [WebHelper]. One can easily adapt it to use dio or any other http client. class HttpFileService extends FileService { final http.Client _httpClient; + final Duration _durationOnMaxAgeZero; - HttpFileService({http.Client? httpClient}) - : _httpClient = httpClient ?? http.Client(); + HttpFileService({http.Client? httpClient, Duration? durationOnMaxAgeZero}) + : _httpClient = httpClient ?? http.Client(), + _durationOnMaxAgeZero = durationOnMaxAgeZero ?? Duration(days: 7); @override Future get(String url, @@ -36,7 +38,7 @@ class HttpFileService extends FileService { } final httpResponse = await _httpClient.send(req); - return HttpGetResponse(httpResponse); + return HttpGetResponse(httpResponse, _durationOnMaxAgeZero); } } @@ -64,12 +66,14 @@ abstract class FileServiceResponse { /// Basic implementation of a [FileServiceResponse] for http requests. class HttpGetResponse implements FileServiceResponse { - HttpGetResponse(this._response); + HttpGetResponse(this._response, this._durationOnMaxAgeZero); final DateTime _receivedTime = clock.now(); final http.StreamedResponse _response; + final Duration _durationOnMaxAgeZero; + @override int get statusCode => _response.statusCode; @@ -86,7 +90,7 @@ class HttpGetResponse implements FileServiceResponse { @override DateTime get validTill { // Without a cache-control header we keep the file for a week - var ageDuration = const Duration(days: 7); + var ageDuration = _durationOnMaxAgeZero; final controlHeader = _header(HttpHeaders.cacheControlHeader); if (controlHeader != null) { final controlSettings = controlHeader.split(','); diff --git a/flutter_cache_manager/lib/src/web/web_helper.dart b/flutter_cache_manager/lib/src/web/web_helper.dart index 20685484..bc588498 100644 --- a/flutter_cache_manager/lib/src/web/web_helper.dart +++ b/flutter_cache_manager/lib/src/web/web_helper.dart @@ -18,9 +18,8 @@ const statusCodesNewFile = [HttpStatus.ok, HttpStatus.accepted]; const statusCodesFileNotChanged = [HttpStatus.notModified]; class WebHelper { - WebHelper(this._store, FileService? fileFetcher) - : _memCache = {}, - fileFetcher = fileFetcher ?? HttpFileService(); + WebHelper(this._store, this.fileFetcher) + : _memCache = {}; final CacheStore _store; @visibleForTesting