This commit is contained in:
thosakwe 2017-08-15 20:01:31 -04:00
parent 2ac66a4877
commit d30edcdef6
5 changed files with 108 additions and 14 deletions

View file

@ -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
Fixed a bug where `Accept-Encoding` was not properly adhered to.

View file

@ -62,6 +62,8 @@ class CachingVirtualDirectory extends VirtualDirectory {
final bool useWeakEtags;
/// The `max-age` for `Cache-Control`.
///
/// Set this to `null` to leave no `Expires` header on responses.
final int maxAge;
CachingVirtualDirectory(
@ -91,7 +93,7 @@ class CachingVirtualDirectory extends VirtualDirectory {
@override
Future<bool> serveFile(
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);
}
@ -154,7 +156,8 @@ class CachingVirtualDirectory extends VirtualDirectory {
generateEtag(buf, weak: useWeakEtags != false, hash: hash);
res.headers
..[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);
if (useWeakEtags == false) {
@ -174,18 +177,21 @@ class CachingVirtualDirectory extends VirtualDirectory {
void setCachedHeaders(
DateTime modified, RequestContext req, ResponseContext res) {
var privacy = accessLevelToString(accessLevel ?? CacheAccessLevel.PUBLIC);
var expiry = new DateTime.now().add(new Duration(seconds: maxAge ?? 0));
res.headers
..[HttpHeaders.CACHE_CONTROL] = '$privacy, max-age=${maxAge ?? 0}'
..[HttpHeaders.EXPIRES] = formatDateForHttp(expiry)
..[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
Future<bool> serveAsset(
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);
}

View file

@ -284,8 +284,17 @@ class VirtualDirectory implements AngelPlugin {
? file.openRead().transform(GZIP.encoder)
: file.openRead();
await stream.pipe(res.io);
} else
await res.sendFile(file);
} 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
await res.sendFile(file);
}
return false;
}
@ -313,7 +322,11 @@ class VirtualDirectory implements AngelPlugin {
: file.content;
await stream.pipe(res.io);
} else {
await file.content.forEach(res.buffer.add);
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);
}
return false;
@ -330,8 +343,8 @@ class VirtualDirectory implements AngelPlugin {
String originalName = file.filename;
for (var transformer in _transformers) {
if (++iterations >= 100) {
print(
'VirtualDirectory has tried 100 times to compile ${file.filename}. Perhaps one of your transformers is not changing the output file\'s extension.');
print('VirtualDirectory has tried 100 times to compile ${file
.filename}. Perhaps one of your transformers is not changing the output file\'s extension.');
throw new AngelHttpException(new StackOverflowError(),
statusCode: 500);
} else if (iterations < 100) iterations++;
@ -363,8 +376,8 @@ class VirtualDirectory implements AngelPlugin {
var compiled = await compileAsset(asset);
if (compiled == null)
p.finish(
message:
'"${entity.absolute.path}" did not require compilation; skipping it.');
message: '"${entity.absolute
.path}" did not require compilation; skipping it.');
else {
var outFile = new File(compiled.filename);
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.');
}
/// 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.');
}
}

View file

@ -4,7 +4,7 @@ environment:
sdk: ">=1.19.0"
homepage: https://github.com/angel-dart/static
author: Tobe O <thosakwe@gmail.com>
version: 1.2.4+1
version: 1.2.5
dependencies:
angel_framework: ^1.0.0-dev
cli_util: ^0.1.1

View file

@ -12,7 +12,7 @@ main() {
Client client = new Client();
setUp(() async {
app = new Angel(debug: true);
app = new Angel();
await app.configure(new VirtualDirectory(
debug: true,
@ -23,6 +23,7 @@ main() {
await app.configure(new VirtualDirectory(
debug: true,
source: testDir,
streamToIO: true,
indexFileNames: ['index.php', 'index.txt']));
app.after.add('Fallback');
@ -72,4 +73,28 @@ main() {
});
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');
});
}