From d4226ab83f665f51de08150e07320deacfaa1239 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 1 Apr 2018 21:05:35 -0400 Subject: [PATCH] Skeleton for ResponseCache --- README.md | 6 +++- lib/angel_cache.dart | 23 ++------------ lib/src/cache.dart | 61 ++++++++++++++++++++++++++++++++++++++ lib/src/cache_service.dart | 21 +++++++++++++ pubspec.yaml | 2 ++ 5 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 lib/src/cache.dart create mode 100644 lib/src/cache_service.dart diff --git a/README.md b/README.md index 3cd87af0..dfacc4a8 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,8 @@ Support for server-side caching in [Angel](https://angel-dart.github.io). *TODO: Documentation* A `Service` class that caches data from one service, storing it in another. -An imaginable use case is storing results from MongoDB or another database in Redis. \ No newline at end of file +An imaginable use case is storing results from MongoDB or another database in +MemcacheD/Redis. + +## `Cache` +*TODO: Documentation* \ No newline at end of file diff --git a/lib/angel_cache.dart b/lib/angel_cache.dart index 3a8ef9cf..faba4152 100644 --- a/lib/angel_cache.dart +++ b/lib/angel_cache.dart @@ -1,21 +1,2 @@ -import 'package:angel_framework/angel_framework.dart'; -import 'package:meta/meta.dart'; - -/// An Angel [Service] that caches data from another service. -/// -/// This is useful for applications of scale, where network latency -/// can have real implications on application performance. -class CacheService extends Service { - /// The underlying [Service] that represents the original data store. - final Service database; - - /// The [Service] used to interface with a caching layer. - /// - /// If not provided, this defaults to a [MapService]. - final Service cache; - - CacheService({@required this.database, Service cache}) - : this.cache = cache ?? new MapService() { - assert(database != null); - } -} +export 'src/cache.dart'; +export 'src/cache_service.dart'; \ No newline at end of file diff --git a/lib/src/cache.dart b/lib/src/cache.dart new file mode 100644 index 00000000..fe4622fb --- /dev/null +++ b/lib/src/cache.dart @@ -0,0 +1,61 @@ +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:intl/intl.dart'; + +final DateFormat _fmt = new DateFormat('EEE, d MMM yyyy HH:mm:ss'); + +/// Formats a date (converted to UTC), ex: `Sun, 03 May 2015 23:02:37 GMT`. +String _formatDateForHttp(DateTime dt) => _fmt.format(dt.toUtc()) + ' GMT'; + +class ResponseCache { + final List patterns = []; + final Duration timeout; + final Map _cache = {}; + + ResponseCache({this.timeout}); + + Future handleRequest(RequestContext req, ResponseContext res) async { + var now = new DateTime.now().toUtc(); + + // Check if there is a cache entry. + for (var pattern in patterns) { + if (pattern.allMatches(req.uri.path).isNotEmpty && _cache.containsKey(req.uri.path)) { + var response = _cache[req.uri.path]; + + // Only send a cached response if it is valid. + if (timeout == null || now.difference(response.timestamp) >= timeout) { + // TODO: If-Last-Modified + break; + } + } + } + + return true; + } + + Future responseFinalizer(RequestContext req, ResponseContext res) async { + var now = new DateTime.now().toUtc(); + } + + void setCachedHeaders( + DateTime modified, RequestContext req, ResponseContext res) { + var privacy = 'public'; + + res.headers + ..['cache-control'] = '$privacy, max-age=${timeout?.inSeconds ?? 0}' + ..['last-modified'] = _formatDateForHttp(modified); + + if (timeout != null) { + var expiry = new DateTime.now().add(timeout); + res.headers['expires'] = _formatDateForHttp(expiry); + } + } +} + +class _CachedResponse { + final Map headers; + final List body; + final DateTime timestamp; + + _CachedResponse({this.headers, this.body, this.timestamp}); +} \ No newline at end of file diff --git a/lib/src/cache_service.dart b/lib/src/cache_service.dart new file mode 100644 index 00000000..3a8ef9cf --- /dev/null +++ b/lib/src/cache_service.dart @@ -0,0 +1,21 @@ +import 'package:angel_framework/angel_framework.dart'; +import 'package:meta/meta.dart'; + +/// An Angel [Service] that caches data from another service. +/// +/// This is useful for applications of scale, where network latency +/// can have real implications on application performance. +class CacheService extends Service { + /// The underlying [Service] that represents the original data store. + final Service database; + + /// The [Service] used to interface with a caching layer. + /// + /// If not provided, this defaults to a [MapService]. + final Service cache; + + CacheService({@required this.database, Service cache}) + : this.cache = cache ?? new MapService() { + assert(database != null); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index c3e683ee..091d738c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,9 @@ name: angel_cache dependencies: angel_framework: ">=1.0.0-dev <2.0.0" + intl: ^0.15.0 meta: ^1.0.0 dev_dependencies: angel_test: ^1.1.0 + glob: ^1.0.0 test: ^0.12.0 \ No newline at end of file