1.2.5
This commit is contained in:
parent
2ac66a4877
commit
d30edcdef6
5 changed files with 108 additions and 14 deletions
|
@ -1,3 +1,10 @@
|
||||||
|
# 1.2.5
|
||||||
|
* Fixed a bug where `onlyInProduction` was not properly adhered to.
|
||||||
|
* Fixed another bug where `Accept-Encoding` was not properly adhered to.
|
||||||
|
* Setting `maxAge` to `null` will now prevent a `CachingVirtualDirectory` from sending an `Expires` header.
|
||||||
|
* Pre-built assets can now be mass-deleted with `VirtualDirectory.cleanFromDisk()`.
|
||||||
|
Resolves [#22](https://github.com/angel-dart/static/issues/22).
|
||||||
|
|
||||||
# 1.2.4+1
|
# 1.2.4+1
|
||||||
Fixed a bug where `Accept-Encoding` was not properly adhered to.
|
Fixed a bug where `Accept-Encoding` was not properly adhered to.
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@ class CachingVirtualDirectory extends VirtualDirectory {
|
||||||
final bool useWeakEtags;
|
final bool useWeakEtags;
|
||||||
|
|
||||||
/// The `max-age` for `Cache-Control`.
|
/// The `max-age` for `Cache-Control`.
|
||||||
|
///
|
||||||
|
/// Set this to `null` to leave no `Expires` header on responses.
|
||||||
final int maxAge;
|
final int maxAge;
|
||||||
|
|
||||||
CachingVirtualDirectory(
|
CachingVirtualDirectory(
|
||||||
|
@ -91,7 +93,7 @@ class CachingVirtualDirectory extends VirtualDirectory {
|
||||||
@override
|
@override
|
||||||
Future<bool> serveFile(
|
Future<bool> serveFile(
|
||||||
File file, FileStat stat, RequestContext req, ResponseContext res) {
|
File file, FileStat stat, RequestContext req, ResponseContext res) {
|
||||||
if (onlyInProduction == true && req.app.isProduction == true) {
|
if (onlyInProduction == true && req.app.isProduction != true) {
|
||||||
return super.serveFile(file, stat, req, res);
|
return super.serveFile(file, stat, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +156,8 @@ class CachingVirtualDirectory extends VirtualDirectory {
|
||||||
generateEtag(buf, weak: useWeakEtags != false, hash: hash);
|
generateEtag(buf, weak: useWeakEtags != false, hash: hash);
|
||||||
res.headers
|
res.headers
|
||||||
..[HttpHeaders.ETAG] = etag
|
..[HttpHeaders.ETAG] = etag
|
||||||
..[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path) ?? 'application/octet-stream';
|
..[HttpHeaders.CONTENT_TYPE] =
|
||||||
|
lookupMimeType(file.path) ?? 'application/octet-stream';
|
||||||
setCachedHeaders(stat.modified, req, res);
|
setCachedHeaders(stat.modified, req, res);
|
||||||
|
|
||||||
if (useWeakEtags == false) {
|
if (useWeakEtags == false) {
|
||||||
|
@ -174,18 +177,21 @@ class CachingVirtualDirectory extends VirtualDirectory {
|
||||||
void setCachedHeaders(
|
void setCachedHeaders(
|
||||||
DateTime modified, RequestContext req, ResponseContext res) {
|
DateTime modified, RequestContext req, ResponseContext res) {
|
||||||
var privacy = accessLevelToString(accessLevel ?? CacheAccessLevel.PUBLIC);
|
var privacy = accessLevelToString(accessLevel ?? CacheAccessLevel.PUBLIC);
|
||||||
var expiry = new DateTime.now().add(new Duration(seconds: maxAge ?? 0));
|
|
||||||
|
|
||||||
res.headers
|
res.headers
|
||||||
..[HttpHeaders.CACHE_CONTROL] = '$privacy, max-age=${maxAge ?? 0}'
|
..[HttpHeaders.CACHE_CONTROL] = '$privacy, max-age=${maxAge ?? 0}'
|
||||||
..[HttpHeaders.EXPIRES] = formatDateForHttp(expiry)
|
|
||||||
..[HttpHeaders.LAST_MODIFIED] = formatDateForHttp(modified);
|
..[HttpHeaders.LAST_MODIFIED] = formatDateForHttp(modified);
|
||||||
|
|
||||||
|
if (maxAge != null) {
|
||||||
|
var expiry = new DateTime.now().add(new Duration(seconds: maxAge ?? 0));
|
||||||
|
res.headers[HttpHeaders.EXPIRES] = formatDateForHttp(expiry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> serveAsset(
|
Future<bool> serveAsset(
|
||||||
FileInfo fileInfo, RequestContext req, ResponseContext res) {
|
FileInfo fileInfo, RequestContext req, ResponseContext res) {
|
||||||
if (onlyInProduction == true && req.app.isProduction == true) {
|
if (onlyInProduction == true && req.app.isProduction != true) {
|
||||||
return super.serveAsset(fileInfo, req, res);
|
return super.serveAsset(fileInfo, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,8 +284,17 @@ class VirtualDirectory implements AngelPlugin {
|
||||||
? file.openRead().transform(GZIP.encoder)
|
? file.openRead().transform(GZIP.encoder)
|
||||||
: file.openRead();
|
: file.openRead();
|
||||||
await stream.pipe(res.io);
|
await stream.pipe(res.io);
|
||||||
|
} else {
|
||||||
|
if (_acceptsGzip(req)) {
|
||||||
|
res.io.headers
|
||||||
|
..set(HttpHeaders.CONTENT_TYPE,
|
||||||
|
lookupMimeType(file.path) ?? 'application/octet-stream')
|
||||||
|
..set(HttpHeaders.CONTENT_ENCODING, 'gzip');
|
||||||
|
await file.openRead().transform(GZIP.encoder).forEach(res.buffer.add);
|
||||||
|
res.end();
|
||||||
} else
|
} else
|
||||||
await res.sendFile(file);
|
await res.sendFile(file);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +322,10 @@ class VirtualDirectory implements AngelPlugin {
|
||||||
: file.content;
|
: file.content;
|
||||||
await stream.pipe(res.io);
|
await stream.pipe(res.io);
|
||||||
} else {
|
} else {
|
||||||
|
if (_acceptsGzip(req)) {
|
||||||
|
res.io.headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip');
|
||||||
|
await file.content.transform(GZIP.encoder).forEach(res.buffer.add);
|
||||||
|
} else
|
||||||
await file.content.forEach(res.buffer.add);
|
await file.content.forEach(res.buffer.add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,8 +343,8 @@ class VirtualDirectory implements AngelPlugin {
|
||||||
String originalName = file.filename;
|
String originalName = file.filename;
|
||||||
for (var transformer in _transformers) {
|
for (var transformer in _transformers) {
|
||||||
if (++iterations >= 100) {
|
if (++iterations >= 100) {
|
||||||
print(
|
print('VirtualDirectory has tried 100 times to compile ${file
|
||||||
'VirtualDirectory has tried 100 times to compile ${file.filename}. Perhaps one of your transformers is not changing the output file\'s extension.');
|
.filename}. Perhaps one of your transformers is not changing the output file\'s extension.');
|
||||||
throw new AngelHttpException(new StackOverflowError(),
|
throw new AngelHttpException(new StackOverflowError(),
|
||||||
statusCode: 500);
|
statusCode: 500);
|
||||||
} else if (iterations < 100) iterations++;
|
} else if (iterations < 100) iterations++;
|
||||||
|
@ -363,8 +376,8 @@ class VirtualDirectory implements AngelPlugin {
|
||||||
var compiled = await compileAsset(asset);
|
var compiled = await compileAsset(asset);
|
||||||
if (compiled == null)
|
if (compiled == null)
|
||||||
p.finish(
|
p.finish(
|
||||||
message:
|
message: '"${entity.absolute
|
||||||
'"${entity.absolute.path}" did not require compilation; skipping it.');
|
.path}" did not require compilation; skipping it.');
|
||||||
else {
|
else {
|
||||||
var outFile = new File(compiled.filename);
|
var outFile = new File(compiled.filename);
|
||||||
if (!await outFile.exists()) await outFile.create(recursive: true);
|
if (!await outFile.exists()) await outFile.create(recursive: true);
|
||||||
|
@ -386,4 +399,47 @@ class VirtualDirectory implements AngelPlugin {
|
||||||
|
|
||||||
print('Build of assets in "${source.absolute.path}" complete.');
|
print('Build of assets in "${source.absolute.path}" complete.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deletes any pre-built assets.
|
||||||
|
Future cleanFromDisk() async {
|
||||||
|
var l = new cli.Logger.standard();
|
||||||
|
print('Cleaning assets in "${source.absolute.path}"...');
|
||||||
|
|
||||||
|
await for (var entity in source.list(recursive: true)) {
|
||||||
|
if (entity is File) {
|
||||||
|
var p = l.progress('Checking "${entity.absolute.path}"');
|
||||||
|
|
||||||
|
try {
|
||||||
|
var asset = new FileInfo.fromFile(entity);
|
||||||
|
var compiled = await compileAsset(asset);
|
||||||
|
if (compiled == null)
|
||||||
|
p.finish(
|
||||||
|
message: '"${entity.absolute
|
||||||
|
.path}" did not require compilation; skipping it.');
|
||||||
|
else {
|
||||||
|
var outFile = new File(compiled.filename);
|
||||||
|
if (await outFile.exists()) {
|
||||||
|
await outFile.delete();
|
||||||
|
p.finish(
|
||||||
|
message: 'Deleted "${compiled
|
||||||
|
.filename}", which was the output of "${entity.absolute
|
||||||
|
.path}".',
|
||||||
|
showTiming: true);
|
||||||
|
} else {
|
||||||
|
p.finish(
|
||||||
|
message:
|
||||||
|
'Output "${compiled.filename}" of "${entity.absolute.path}" does not exist.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on AngelHttpException {
|
||||||
|
// Ignore 500
|
||||||
|
} catch (e, st) {
|
||||||
|
p.finish(message: 'Failed to delete "${entity.absolute.path}".');
|
||||||
|
stderr..writeln(e)..writeln(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('Purge of assets in "${source.absolute.path}" complete.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0"
|
||||||
homepage: https://github.com/angel-dart/static
|
homepage: https://github.com/angel-dart/static
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
version: 1.2.4+1
|
version: 1.2.5
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.0.0-dev
|
angel_framework: ^1.0.0-dev
|
||||||
cli_util: ^0.1.1
|
cli_util: ^0.1.1
|
||||||
|
|
|
@ -12,7 +12,7 @@ main() {
|
||||||
Client client = new Client();
|
Client client = new Client();
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = new Angel(debug: true);
|
app = new Angel();
|
||||||
|
|
||||||
await app.configure(new VirtualDirectory(
|
await app.configure(new VirtualDirectory(
|
||||||
debug: true,
|
debug: true,
|
||||||
|
@ -23,6 +23,7 @@ main() {
|
||||||
await app.configure(new VirtualDirectory(
|
await app.configure(new VirtualDirectory(
|
||||||
debug: true,
|
debug: true,
|
||||||
source: testDir,
|
source: testDir,
|
||||||
|
streamToIO: true,
|
||||||
indexFileNames: ['index.php', 'index.txt']));
|
indexFileNames: ['index.php', 'index.txt']));
|
||||||
|
|
||||||
app.after.add('Fallback');
|
app.after.add('Fallback');
|
||||||
|
@ -72,4 +73,28 @@ main() {
|
||||||
});
|
});
|
||||||
expect(response.body, equals("index!"));
|
expect(response.body, equals("index!"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('can gzip: just gzip', () async {
|
||||||
|
var response = await client
|
||||||
|
.get("$url/sample.txt", headers: {HttpHeaders.ACCEPT_ENCODING: 'gzip'});
|
||||||
|
expect(response.body, equals("Hello world"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_ENCODING], 'gzip');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can gzip: wildcard', () async {
|
||||||
|
var response = await client
|
||||||
|
.get("$url/sample.txt", headers: {HttpHeaders.ACCEPT_ENCODING: 'foo, *'});
|
||||||
|
expect(response.body, equals("Hello world"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_ENCODING], 'gzip');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can gzip: gzip and friends', () async {
|
||||||
|
var response = await client
|
||||||
|
.get("$url/sample.txt", headers: {HttpHeaders.ACCEPT_ENCODING: 'gzip, deflate, br'});
|
||||||
|
expect(response.body, equals("Hello world"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_ENCODING], 'gzip');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue