Updated cache
This commit is contained in:
parent
0a456954b4
commit
c9c431b3e5
14 changed files with 164 additions and 68 deletions
|
@ -13,7 +13,7 @@
|
||||||
* Migrated angel_model to 3.0.0 (0/0 tests passed)
|
* Migrated angel_model to 3.0.0 (0/0 tests passed)
|
||||||
* Migrated angel_container to 3.0.0 (55/55 tests passed)
|
* Migrated angel_container to 3.0.0 (55/55 tests passed)
|
||||||
* Added merge_map and migrated to 2.0.0 (6/6 tests passed)
|
* Added merge_map and migrated to 2.0.0 (6/6 tests passed)
|
||||||
* Added mock_request and migrated to 2.0.0 (0/0 tests)
|
* Added mock_request and migrated to 2.0.0 (5/5 tests)
|
||||||
* Migrated angel_framework to 4.0.0 (149/150 tests passed)
|
* Migrated angel_framework to 4.0.0 (149/150 tests passed)
|
||||||
* Migrated angel_auth to 4.0.0 (27/30 tests passed)
|
* Migrated angel_auth to 4.0.0 (27/30 tests passed)
|
||||||
* Migrated angel_configuration to 4.0.0 (6/8 testspassed)
|
* Migrated angel_configuration to 4.0.0 (6/8 testspassed)
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
* Migrated angel_orm_postgres to 3.0.0 (51/54 tests passed)
|
* Migrated angel_orm_postgres to 3.0.0 (51/54 tests passed)
|
||||||
* Create orm-sdk-2.12.x boilerplate (in progress) <= Milestone 2
|
* Create orm-sdk-2.12.x boilerplate (in progress) <= Milestone 2
|
||||||
* Migrated angel_auth_oauth2 to 4.0.0 (0/0 tests passed)
|
* Migrated angel_auth_oauth2 to 4.0.0 (0/0 tests passed)
|
||||||
* Migrated angel_auth_cache to 4.0.0 (0/7 tests passed)
|
* Migrated angel_auth_cache to 4.0.0 (4/7 tests passed)
|
||||||
* Migrated angel_auth_cors to 4.0.0 (10/15 tests passed)
|
* Migrated angel_auth_cors to 4.0.0 (10/15 tests passed)
|
||||||
* Migrated angel_oauth2 to 4.0.0 (17/25 tests passed)
|
* Migrated angel_oauth2 to 4.0.0 (17/25 tests passed)
|
||||||
* Migrated angel_proxy to 4.0.0 (5/7 tests passed)
|
* Migrated angel_proxy to 4.0.0 (5/7 tests passed)
|
||||||
|
|
|
@ -1,3 +1,25 @@
|
||||||
# Contributing Angel3
|
|
||||||
|
|
||||||
Any contributions from the community are welcome.
|
# Contribution
|
||||||
|
|
||||||
|
Any help from the open-source community is always welcome and needed:
|
||||||
|
|
||||||
|
1. Found an issue?
|
||||||
|
- Please [fill a bug report][tracker] with error message and steps to reproduce it.
|
||||||
|
2. Wish a feature?
|
||||||
|
- Open a feature request with use cases.
|
||||||
|
3. Are you using and liking the project?
|
||||||
|
- Create an article about your use case
|
||||||
|
- Do a post on your likes and dislikes
|
||||||
|
- Make a donation.
|
||||||
|
4. Are you a developer?
|
||||||
|
- Fix a bug and send a [pull request][pull_request]
|
||||||
|
- Implement a new feature
|
||||||
|
- Improve the Unit Tests
|
||||||
|
- Improve the [User Guide][doc] and send a [document pull request][doc_repo]
|
||||||
|
5. Have you already helped in any way?
|
||||||
|
- **Many thanks to the contributors and everybody that uses this project!**
|
||||||
|
|
||||||
|
[tracker]: https://github.com/dukefirehawk/angel/issues
|
||||||
|
[pull_request]: https://github.com/dukefirehawk/angel/pulls
|
||||||
|
[doc]: https://angel3-docs.dukefirehawk.com
|
||||||
|
[doc_repo]: https://github.com/dukefirehawk/angel3-guide/pulls
|
||||||
|
|
|
@ -93,7 +93,7 @@ Check out [Migrating to Angel3](https://angel3-docs.dukefirehawk.com/migration/a
|
||||||
|
|
||||||
## Examples and Documentation
|
## Examples and Documentation
|
||||||
|
|
||||||
Visit the [documentation](https://angel3-docs.dukefirehawk.com/) for dozens of guides and resources, including video tutorials, to get up and running as quickly as possible with Angel3 framework.
|
Visit the [User Guide](https://angel3-docs.dukefirehawk.com/) for dozens of guides and resources, including video tutorials, to get up and running as quickly as possible with Angel3 framework.
|
||||||
|
|
||||||
Examples and complete projects can be found [here](https://github.com/dukefirehawk/angel3-examples).
|
Examples and complete projects can be found [here](https://github.com/dukefirehawk/angel3-examples).
|
||||||
|
|
||||||
|
@ -103,4 +103,4 @@ There is also an [Awesome Angel :fire:](https://github.com/dukefirehawk/angel3-a
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Interested in contributing to Angel3? Start by reading the contribution guide [here](CONTRIBUTING.md).
|
Interested in contributing to Angel3? See the contribution guide [here](CONTRIBUTING.md).
|
||||||
|
|
16
TODO.md
16
TODO.md
|
@ -1,9 +1,13 @@
|
||||||
### angel_framework
|
# Road Map
|
||||||
* Migrate http_server to shelf
|
|
||||||
### Container/angel_container_generator
|
|
||||||
|
|
||||||
* test/reflector_test.reflectab.dart - Changed ImplicitGetterMirrorImpl() from 5 to 3 parameters (revisit later)
|
## Short Term Goal
|
||||||
* A user forum
|
|
||||||
* Updated User Guide
|
|
||||||
|
|
||||||
|
* Migrate all modules to support NNBD
|
||||||
|
|
||||||
|
* Add more examples
|
||||||
|
|
||||||
|
* Improve User Guide
|
||||||
|
|
||||||
|
## Long Term Goal
|
||||||
|
|
||||||
|
* Upgrade Angel3 architecture
|
||||||
|
|
2
packages/cache/CHANGELOG.md
vendored
2
packages/cache/CHANGELOG.md
vendored
|
@ -3,6 +3,8 @@
|
||||||
## 4.0.1
|
## 4.0.1
|
||||||
|
|
||||||
* Updated pubspec description
|
* Updated pubspec description
|
||||||
|
* Fixed: Return `200` with cached data instead of `403`
|
||||||
|
* Updated broken Unit Tests
|
||||||
|
|
||||||
## 4.0.0
|
## 4.0.0
|
||||||
|
|
||||||
|
|
7
packages/cache/README.md
vendored
7
packages/cache/README.md
vendored
|
@ -1,4 +1,4 @@
|
||||||
# Angel3 Cache
|
# HTTP Caching for Angel3
|
||||||
|
|
||||||
[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_cache)
|
[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_cache)
|
||||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/cache/LICENSE)
|
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/cache/LICENSE)
|
||||||
|
|
||||||
Support for server-side caching in [Angel3](https://github.com/dukefirehawk/angel).
|
A service that provides HTTP caching to the response data for [Angel3](https://github.com/dukefirehawk/angel).
|
||||||
|
|
||||||
## `CacheService`
|
## `CacheService`
|
||||||
|
|
||||||
|
@ -16,8 +16,7 @@ A `Service` class that caches data from one service, storing it in another. An i
|
||||||
|
|
||||||
A middleware that enables the caching of response serialization.
|
A middleware that enables the caching of response serialization.
|
||||||
|
|
||||||
This can improve the performance of sending objects that are complex to serialize.
|
This can improve the performance of sending objects that are complex to serialize. You can pass a [shouldCache] callback to determine which values should be cached.
|
||||||
You can pass a [shouldCache] callback to determine which values should be cached.
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
void main() async {
|
void main() async {
|
||||||
|
|
7
packages/cache/example/main.dart
vendored
7
packages/cache/example/main.dart
vendored
|
@ -1,7 +1,6 @@
|
||||||
import 'package:angel3_cache/angel3_cache.dart';
|
import 'package:angel3_cache/angel3_cache.dart';
|
||||||
import 'package:angel3_framework/angel3_framework.dart';
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
import 'package:angel3_framework/http.dart';
|
import 'package:angel3_framework/http.dart';
|
||||||
import 'package:glob/glob.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
var app = Angel();
|
var app = Angel();
|
||||||
|
@ -9,7 +8,7 @@ void main() async {
|
||||||
// Cache a glob
|
// Cache a glob
|
||||||
var cache = ResponseCache()
|
var cache = ResponseCache()
|
||||||
..patterns.addAll([
|
..patterns.addAll([
|
||||||
Glob('/*.txt'),
|
RegExp('^/?\\w+\\.txt'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Handle `if-modified-since` header, and also send cached content
|
// Handle `if-modified-since` header, and also send cached content
|
||||||
|
@ -21,7 +20,9 @@ void main() async {
|
||||||
|
|
||||||
// Support purging the cache.
|
// Support purging the cache.
|
||||||
app.addRoute('PURGE', '*', (req, res) {
|
app.addRoute('PURGE', '*', (req, res) {
|
||||||
if (req.ip != '127.0.0.1') throw AngelHttpException.forbidden();
|
if (req.ip != '127.0.0.1') {
|
||||||
|
throw AngelHttpException.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
cache.purge(req.uri!.path);
|
cache.purge(req.uri!.path);
|
||||||
print('Purged ${req.uri!.path}');
|
print('Purged ${req.uri!.path}');
|
||||||
|
|
71
packages/cache/lib/src/cache.dart
vendored
71
packages/cache/lib/src/cache.dart
vendored
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:io' show HttpDate;
|
import 'dart:io' show HttpDate;
|
||||||
import 'package:angel3_framework/angel3_framework.dart';
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
import 'package:pool/pool.dart';
|
import 'package:pool/pool.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
/// A flexible response cache for Angel.
|
/// A flexible response cache for Angel.
|
||||||
///
|
///
|
||||||
|
@ -22,6 +23,8 @@ class ResponseCache {
|
||||||
/// If `true` (default: `false`), then caching of results will discard URI query parameters and fragments.
|
/// If `true` (default: `false`), then caching of results will discard URI query parameters and fragments.
|
||||||
final bool ignoreQueryAndFragment;
|
final bool ignoreQueryAndFragment;
|
||||||
|
|
||||||
|
final log = Logger('ResponseCache');
|
||||||
|
|
||||||
ResponseCache(
|
ResponseCache(
|
||||||
{this.timeout = const Duration(minutes: 10),
|
{this.timeout = const Duration(minutes: 10),
|
||||||
this.ignoreQueryAndFragment = false});
|
this.ignoreQueryAndFragment = false});
|
||||||
|
@ -38,26 +41,41 @@ class ResponseCache {
|
||||||
///
|
///
|
||||||
/// This prevents the server from even having to access the cache, and plays very well with static assets.
|
/// This prevents the server from even having to access the cache, and plays very well with static assets.
|
||||||
Future<bool> ifModifiedSince(RequestContext req, ResponseContext res) async {
|
Future<bool> ifModifiedSince(RequestContext req, ResponseContext res) async {
|
||||||
if (req.method != 'GET' && req.method != 'HEAD') return true;
|
if (req.method != 'GET' && req.method != 'HEAD') {
|
||||||
|
return true;
|
||||||
if (req.headers!.ifModifiedSince != null) {
|
}
|
||||||
var modifiedSince = req.headers!.ifModifiedSince;
|
|
||||||
|
|
||||||
|
var modifiedSince = req.headers?.ifModifiedSince;
|
||||||
|
if (modifiedSince != null) {
|
||||||
// Check if there is a cache entry.
|
// Check if there is a cache entry.
|
||||||
for (var pattern in patterns) {
|
for (var pattern in patterns) {
|
||||||
if (pattern.allMatches(_getEffectivePath(req)).isNotEmpty &&
|
var reqPath = _getEffectivePath(req);
|
||||||
_cache.containsKey(_getEffectivePath(req))) {
|
|
||||||
var response = _cache[_getEffectivePath(req)]!;
|
|
||||||
//print('timestamp ${response.timestamp} vs since ${modifiedSince}');
|
|
||||||
|
|
||||||
if (response.timestamp.compareTo(modifiedSince!) <= 0) {
|
if (pattern.allMatches(reqPath).isNotEmpty &&
|
||||||
|
_cache.containsKey(reqPath)) {
|
||||||
|
var response = _cache[reqPath];
|
||||||
|
|
||||||
|
//log.info('timestamp ${response?.timestamp} vs since $modifiedSince');
|
||||||
|
|
||||||
|
if (response != null &&
|
||||||
|
response.timestamp.compareTo(modifiedSince) <= 0) {
|
||||||
// If the cache timeout has been met, don't send the cached response.
|
// If the cache timeout has been met, don't send the cached response.
|
||||||
if (DateTime.now().toUtc().difference(response.timestamp) >=
|
var timeDiff =
|
||||||
timeout) {
|
DateTime.now().toUtc().difference(response.timestamp);
|
||||||
|
|
||||||
|
//log.info(
|
||||||
|
// 'Time Diff: ${timeDiff.inMilliseconds} >= ${timeout.inMilliseconds}');
|
||||||
|
if (timeDiff.inMilliseconds >= timeout.inMilliseconds) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.statusCode = 304;
|
// Old code: res.statusCode = 304;
|
||||||
|
// Return the response stored in the cache
|
||||||
|
_setCachedHeaders(response.timestamp, req, res);
|
||||||
|
res
|
||||||
|
..headers.addAll(response.headers)
|
||||||
|
..add(response.body);
|
||||||
|
await res.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +85,13 @@ class ResponseCache {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getEffectivePath(RequestContext req) =>
|
String _getEffectivePath(RequestContext req) {
|
||||||
ignoreQueryAndFragment == true ? req.uri!.path : req.uri.toString();
|
if (req.uri == null) {
|
||||||
|
log.severe('Request URI is null');
|
||||||
|
throw ArgumentError('Request URI is null');
|
||||||
|
}
|
||||||
|
return ignoreQueryAndFragment == true ? req.uri!.path : req.uri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/// Serves content from the cache, if applicable.
|
/// Serves content from the cache, if applicable.
|
||||||
Future<bool> handleRequest(RequestContext req, ResponseContext res) async {
|
Future<bool> handleRequest(RequestContext req, ResponseContext res) async {
|
||||||
|
@ -114,37 +137,41 @@ class ResponseCache {
|
||||||
if (res.statusCode == 304) {
|
if (res.statusCode == 304) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.method != 'GET' && req.method != 'HEAD') {
|
if (req.method != 'GET' && req.method != 'HEAD') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is a cache entry.
|
// Check if there is a cache entry.
|
||||||
for (var pattern in patterns) {
|
for (var pattern in patterns) {
|
||||||
if (pattern.allMatches(_getEffectivePath(req)).isNotEmpty) {
|
var reqPath = _getEffectivePath(req);
|
||||||
|
|
||||||
|
if (pattern.allMatches(reqPath).isNotEmpty) {
|
||||||
var now = DateTime.now().toUtc();
|
var now = DateTime.now().toUtc();
|
||||||
|
|
||||||
// Invalidate the response, if need be.
|
// Invalidate the response, if need be.
|
||||||
if (_cache.containsKey(_getEffectivePath(req))) {
|
if (_cache.containsKey(reqPath)) {
|
||||||
// If there is no timeout, don't invalidate.
|
// If there is no timeout, don't invalidate.
|
||||||
//if (timeout == null) return true;
|
//if (timeout == null) return true;
|
||||||
|
|
||||||
// Otherwise, don't invalidate unless the timeout has been exceeded.
|
// Otherwise, don't invalidate unless the timeout has been exceeded.
|
||||||
var response = _cache[_getEffectivePath(req)];
|
var response = _cache[reqPath];
|
||||||
if (response == null ||
|
if (response == null ||
|
||||||
now.difference(response.timestamp) < timeout) {
|
now.difference(response.timestamp) < timeout) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cache entry should be invalidated, then invalidate it.
|
// If the cache entry should be invalidated, then invalidate it.
|
||||||
purge(_getEffectivePath(req));
|
purge(reqPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the response.
|
// Save the response.
|
||||||
var writeLock =
|
var writeLock = _writeLocks.putIfAbsent(reqPath, () => Pool(1));
|
||||||
_writeLocks.putIfAbsent(_getEffectivePath(req), () => Pool(1));
|
|
||||||
await writeLock.withResource(() {
|
await writeLock.withResource(() {
|
||||||
_cache[_getEffectivePath(req)] = _CachedResponse(
|
if (res.buffer != null) {
|
||||||
Map.from(res.headers), res.buffer!.toBytes(), now);
|
_cache[reqPath] = _CachedResponse(
|
||||||
|
Map.from(res.headers), res.buffer!.toBytes(), now);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_setCachedHeaders(now, req, res);
|
_setCachedHeaders(now, req, res);
|
||||||
|
|
2
packages/cache/lib/src/cache_service.dart
vendored
2
packages/cache/lib/src/cache_service.dart
vendored
|
@ -49,7 +49,7 @@ class CacheService<Id, Data> extends Service<Id, Data> {
|
||||||
var queryEqual = ignoreParams == true ||
|
var queryEqual = ignoreParams == true ||
|
||||||
(cached.params != null &&
|
(cached.params != null &&
|
||||||
const MapEquality().equals(
|
const MapEquality().equals(
|
||||||
params['query'] as Map?, cached.params['query'] as Map?));
|
params['query'] as Map, cached.params['query'] as Map));
|
||||||
if (queryEqual) {
|
if (queryEqual) {
|
||||||
return await getCached();
|
return await getCached();
|
||||||
}
|
}
|
||||||
|
|
2
packages/cache/pubspec.yaml
vendored
2
packages/cache/pubspec.yaml
vendored
|
@ -1,6 +1,6 @@
|
||||||
name: angel3_cache
|
name: angel3_cache
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
description: A plugin service that support server-side caching of reponse data for Angel3.
|
description: A service that provides HTTP caching to the response data for Angel3
|
||||||
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/cache
|
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/cache
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.12.0 <3.0.0'
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
69
packages/cache/test/cache_test.dart
vendored
69
packages/cache/test/cache_test.dart
vendored
|
@ -4,66 +4,82 @@ import 'package:angel3_cache/angel3_cache.dart';
|
||||||
import 'package:angel3_framework/angel3_framework.dart';
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
import 'package:angel3_test/angel3_test.dart';
|
import 'package:angel3_test/angel3_test.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:glob/glob.dart';
|
//import 'package:glob/glob.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
Logger.root.level = Level.ALL;
|
||||||
|
Logger.root.onRecord.listen((record) {
|
||||||
|
print(
|
||||||
|
'${record.time}: ${record.level.name}: ${record.loggerName}: ${record.message}');
|
||||||
|
});
|
||||||
|
|
||||||
void main() async {
|
|
||||||
group('no timeout', () {
|
group('no timeout', () {
|
||||||
late TestClient client;
|
late TestClient client;
|
||||||
late DateTime lastModified;
|
DateTime? lastModified;
|
||||||
late http.Response response1, response2;
|
late http.Response response1, response2;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
var app = Angel();
|
var app = Angel();
|
||||||
var cache = ResponseCache()
|
var cache = ResponseCache()
|
||||||
..patterns.addAll([
|
..patterns.addAll([
|
||||||
Glob('/*.txt'),
|
//Glob('/*.txt'), // Requires to create folders and files for testing
|
||||||
|
RegExp('^/?\\w+\\.txt'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
app.fallback(cache.handleRequest);
|
app.fallback(cache.handleRequest);
|
||||||
|
|
||||||
app.get('/date.txt', (req, res) {
|
app.get('/date.txt', (req, res) {
|
||||||
|
var data = DateTime.now().toIso8601String();
|
||||||
|
print('Res data: $data');
|
||||||
res
|
res
|
||||||
..useBuffer()
|
..useBuffer()
|
||||||
..write(DateTime.now().toIso8601String());
|
..write(data);
|
||||||
print('req----->');
|
print('Generate results...');
|
||||||
print(req.headers);
|
|
||||||
print('res----->');
|
|
||||||
print(res.headers);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.addRoute('PURGE', '*', (req, res) {
|
app.addRoute('PURGE', '*', (req, res) {
|
||||||
cache.purge(req.uri!.path);
|
if (req.uri != null) {
|
||||||
print('Purged ${req.uri!.path}');
|
cache.purge(req.uri!.path);
|
||||||
|
print('Purged ${req.uri!.path}');
|
||||||
|
} else {
|
||||||
|
print('req.uri is null');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.responseFinalizers.add(cache.responseFinalizer);
|
app.responseFinalizers.add(cache.responseFinalizer);
|
||||||
|
|
||||||
var oldHandler = app.errorHandler;
|
var oldHandler = app.errorHandler;
|
||||||
app.errorHandler = (e, req, res) {
|
app.errorHandler = (e, req, res) {
|
||||||
if (e.error == null) return oldHandler(e, req, res);
|
if (e.error == null) {
|
||||||
|
return oldHandler(e, req, res);
|
||||||
|
}
|
||||||
return Zone.current
|
return Zone.current
|
||||||
.handleUncaughtError(e.error as Object, e.stackTrace!);
|
.handleUncaughtError(e.error as Object, e.stackTrace!);
|
||||||
};
|
};
|
||||||
|
|
||||||
client = await connectTo(app);
|
client = await connectTo(app);
|
||||||
response1 = await client.get(Uri.parse('/date.txt'));
|
response1 = await client.get(Uri.parse('/date.txt'));
|
||||||
response2 = await client.get(Uri.parse('/date.txt'));
|
|
||||||
print(response2.headers);
|
|
||||||
lastModified = DateTime.now();
|
|
||||||
//lastModified = HttpDate.parse(response2.headers['last-modified'] ?? '');
|
|
||||||
print('Response 1 status: ${response1.statusCode}');
|
print('Response 1 status: ${response1.statusCode}');
|
||||||
print('Response 2 status: ${response2.statusCode}');
|
|
||||||
print('Response 1 body: ${response1.body}');
|
|
||||||
print('Response 2 body: ${response2.body}');
|
|
||||||
print('Response 1 headers: ${response1.headers}');
|
print('Response 1 headers: ${response1.headers}');
|
||||||
|
print('Response 1 body: ${response1.body}');
|
||||||
|
|
||||||
|
response2 = await client.get(Uri.parse('/date.txt'));
|
||||||
|
print('Response 2 status: ${response2.statusCode}');
|
||||||
print('Response 2 headers: ${response2.headers}');
|
print('Response 2 headers: ${response2.headers}');
|
||||||
|
print('Response 2 body: ${response2.body}');
|
||||||
|
if (response2.headers['last-modified'] == null) {
|
||||||
|
print('last-modified is null');
|
||||||
|
} else {
|
||||||
|
lastModified = HttpDate.parse(response2.headers['last-modified']!);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() => client.close());
|
tearDown(() => client.close());
|
||||||
|
|
||||||
test('saves content', () async {
|
test('saves content', () async {
|
||||||
expect(response1.body, response2.body);
|
expect(response2.body, response1.body);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('saves headers', () async {
|
test('saves headers', () async {
|
||||||
|
@ -88,20 +104,25 @@ void main() async {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sends 304 on if-modified-since', () async {
|
test('sends 304 on if-modified-since', () async {
|
||||||
|
lastModified ??= DateTime.now();
|
||||||
var headers = {
|
var headers = {
|
||||||
'if-modified-since':
|
'if-modified-since':
|
||||||
HttpDate.format(lastModified.add(const Duration(days: 1)))
|
HttpDate.format(lastModified!.add(const Duration(days: 1)))
|
||||||
};
|
};
|
||||||
var response = await client.get(Uri.parse('/date.txt'), headers: headers);
|
var response = await client.get(Uri.parse('/date.txt'), headers: headers);
|
||||||
print('Sending headers: $headers');
|
print('Sending headers: $headers');
|
||||||
print('Response (${response.statusCode}): ${response.headers}');
|
print('Response status: ${response.statusCode})');
|
||||||
expect(response.statusCode, 304);
|
print('Response headers: ${response.headers}');
|
||||||
|
print('Response body: ${response.body}');
|
||||||
|
//expect(response.statusCode, 304);
|
||||||
|
expect(response.statusCode, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('last-modified in the past', () async {
|
test('last-modified in the past', () async {
|
||||||
|
lastModified ??= DateTime.now();
|
||||||
var response = await client.get(Uri.parse('/date.txt'), headers: {
|
var response = await client.get(Uri.parse('/date.txt'), headers: {
|
||||||
'if-modified-since':
|
'if-modified-since':
|
||||||
HttpDate.format(lastModified.subtract(const Duration(days: 10)))
|
HttpDate.format(lastModified!.subtract(const Duration(days: 10)))
|
||||||
});
|
});
|
||||||
print('Response: ${response.body}');
|
print('Response: ${response.body}');
|
||||||
expect(response.statusCode, 200);
|
expect(response.statusCode, 200);
|
||||||
|
|
0
packages/cache/test/files/date.txt
vendored
Normal file
0
packages/cache/test/files/date.txt
vendored
Normal file
21
packages/cache/test/pattern_test.dart
vendored
Normal file
21
packages/cache/test/pattern_test.dart
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:glob/glob.dart';
|
||||||
|
import 'package:glob/list_local_fs.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
/*
|
||||||
|
var filePat = Glob('**.txt');
|
||||||
|
for (var entity in filePat.listSync()) {
|
||||||
|
print(entity.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = filePat.allMatches(path);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
var path = "ababa99.txt";
|
||||||
|
//var regPat = RegExp('\w+\.txt');
|
||||||
|
var regPat = RegExp('^/?\\w+\\.txt');
|
||||||
|
var result = regPat.allMatches(path);
|
||||||
|
|
||||||
|
print(result.length);
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
PyGithub
|
|
Loading…
Reference in a new issue