This commit is contained in:
Tobe O 2019-05-02 19:29:09 -04:00
parent 42779a85e4
commit fd53da891f
12 changed files with 85 additions and 73 deletions

View file

@ -1,3 +1,8 @@
# 2.1.3
* Apply lints.
* Pin to Dart `>=2.0.0 <3.0.0`.
* Use at least version `2.0.0-rc.0` of `angel_framework`.
# 2.1.2+1
* Fix a typo that prevented `Range` requests from working.

View file

@ -25,20 +25,20 @@ import 'package:angel_static/angel_static.dart';
import 'package:file/local.dart';
main() async {
var app = new Angel();
var app = Angel();
var fs = const LocalFileSystem();
// Normal static server
var vDir = new VirtualDirectory(app, fs, source: new Directory('./public'));
var vDir = VirtualDirectory(app, fs, source: Directory('./public'));
// Send Cache-Control, ETag, etc. as well
var vDir = new CachingVirtualDirectory(app, fs, source: new Directory('./public'));
var vDir = CachingVirtualDirectory(app, fs, source: Directory('./public'));
// Mount the VirtualDirectory's request handler
app.fallback(vDir.handleRequest);
// Start your server!!!
await new AngelHttp(app).startServer();
await AngelHttp(app).startServer();
}
```
@ -49,7 +49,7 @@ the user is requesting that file. This can be very useful for SPA's.
```dart
// Create VirtualDirectory as well
var vDir = new CachingVirtualDirectory(...);
var vDir = CachingVirtualDirectory(...);
// Mount it
app.fallback(vDir.handleRequest);

View file

@ -1,3 +1,8 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false
implicit-casts: false
linter:
rules:
- unnecessary_const
- unnecessary_new

View file

@ -5,10 +5,10 @@ import 'package:file/local.dart';
import 'package:logging/logging.dart';
main(List<String> args) async {
var app = new Angel();
var http = new AngelHttp(app);
var app = Angel();
var http = AngelHttp(app);
var fs = const LocalFileSystem();
var vDir = new CachingVirtualDirectory(
var vDir = CachingVirtualDirectory(
app,
fs,
allowDirectoryListing: true,
@ -24,7 +24,7 @@ main(List<String> args) async {
..addExtension('md', 'text/plain')
..addExtension('yaml', 'text/plain');
app.logger = new Logger('example')
app.logger = Logger('example')
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
@ -32,6 +32,7 @@ main(List<String> args) async {
});
app.fallback(vDir.handleRequest);
app.fallback((req, res) => throw AngelHttpException.notFound());
var server = await http.startServer('127.0.0.1', 3000);
print('Serving from ${vDir.source.path}');

View file

@ -12,7 +12,7 @@ String accessLevelToString(CacheAccessLevel accessLevel) {
case CacheAccessLevel.PUBLIC:
return 'public';
default:
throw new ArgumentError('Unrecognized cache access level: $accessLevel');
throw ArgumentError('Unrecognized cache access level: $accessLevel');
}
}
@ -38,16 +38,16 @@ class CachingVirtualDirectory extends VirtualDirectory {
final int maxAge;
CachingVirtualDirectory(Angel app, FileSystem fileSystem,
{this.accessLevel: CacheAccessLevel.PUBLIC,
{this.accessLevel = CacheAccessLevel.PUBLIC,
Directory source,
bool debug,
Iterable<String> indexFileNames,
this.maxAge: 0,
this.noCache: false,
this.onlyInProduction: false,
this.useEtags: true,
this.maxAge = 0,
this.noCache = false,
this.onlyInProduction = false,
this.useEtags = true,
bool allowDirectoryListing,
bool useBuffer: false,
bool useBuffer = false,
String publicPath,
callback(File file, RequestContext req, ResponseContext res)})
: super(app, fileSystem,
@ -63,7 +63,7 @@ class CachingVirtualDirectory extends VirtualDirectory {
File file, FileStat stat, RequestContext req, ResponseContext res) {
res.headers['accept-ranges'] = 'bytes';
if (onlyInProduction == true && req.app.isProduction != true) {
if (onlyInProduction == true && req.app.environment.isProduction != true) {
return super.serveFile(file, stat, req, res);
}
@ -105,12 +105,12 @@ class CachingVirtualDirectory extends VirtualDirectory {
return super.serveFile(file, stat, req, res);
}
return new Future.value(false);
return Future.value(false);
} else if (ifRange) {
return super.serveFile(file, stat, req, res);
}
} catch (_) {
throw new AngelHttpException.badRequest(
throw AngelHttpException.badRequest(
message:
'Invalid date for ${ifRange ? 'if-range' : 'if-not-modified-since'} header.');
}
@ -143,7 +143,7 @@ class CachingVirtualDirectory extends VirtualDirectory {
if (!hasBeenModified) {
res.statusCode = 304;
setCachedHeaders(stat.modified, req, res);
return new Future.value(false);
return Future.value(false);
}
} else {
return super.serveFile(file, stat, req, res);
@ -172,7 +172,7 @@ class CachingVirtualDirectory extends VirtualDirectory {
..['last-modified'] = HttpDate.format(modified);
if (maxAge != null) {
var expiry = new DateTime.now().add(new Duration(seconds: maxAge ?? 0));
var expiry = DateTime.now().add(Duration(seconds: maxAge ?? 0));
res.headers['expires'] = HttpDate.format(expiry);
}
}

View file

@ -5,8 +5,8 @@ import 'package:http_parser/http_parser.dart';
import 'package:path/path.dart' as p;
import 'package:range_header/range_header.dart';
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
final RegExp _param = RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
String _pathify(String path) {
var p = path.replaceAll(_straySlashes, '');
@ -54,16 +54,16 @@ class VirtualDirectory {
VirtualDirectory(this.app, this.fileSystem,
{Directory source,
this.indexFileNames: const ['index.html'],
this.publicPath: '/',
this.indexFileNames = const ['index.html'],
this.publicPath = '/',
this.callback,
this.allowDirectoryListing: false,
this.useBuffer: false}) {
this.allowDirectoryListing = false,
this.useBuffer = false}) {
_prefix = publicPath.replaceAll(_straySlashes, '');
if (source != null) {
_source = source;
} else {
String dirPath = app.isProduction ? './build/web' : './web';
String dirPath = app.environment.isProduction ? './build/web' : './web';
_source = fileSystem.directory(dirPath);
}
}
@ -71,11 +71,11 @@ class VirtualDirectory {
/// Responds to incoming HTTP requests.
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
if (req.method != 'GET' && req.method != 'HEAD')
return new Future<bool>.value(true);
return Future<bool>.value(true);
var path = req.uri.path.replaceAll(_straySlashes, '');
if (_prefix?.isNotEmpty == true && !path.startsWith(_prefix))
return new Future<bool>.value(true);
return Future<bool>.value(true);
return servePath(path, req, res);
}
@ -91,11 +91,11 @@ class VirtualDirectory {
return (RequestContext req, ResponseContext res) {
var path = req.path.replaceAll(_straySlashes, '');
if (path == vPath) return new Future<bool>.value(true);
if (path == vPath) return Future<bool>.value(true);
if (accepts?.isNotEmpty == true) {
if (!accepts.any((x) => req.accepts(x, strict: true)))
return new Future<bool>.value(true);
return Future<bool>.value(true);
}
return servePath(vPath, req, res);
@ -108,7 +108,7 @@ class VirtualDirectory {
if (_prefix.isNotEmpty) {
// Only replace the *first* incidence
// Resolve: https://github.com/angel-dart/angel/issues/41
path = path.replaceFirst(new RegExp('^' + _pathify(_prefix)), '');
path = path.replaceFirst(RegExp('^' + _pathify(_prefix)), '');
}
if (path.isEmpty) path = '.';
@ -151,7 +151,7 @@ class VirtualDirectory {
}
if (allowDirectoryListing == true) {
res.contentType = new MediaType('text', 'html');
res.contentType = MediaType('text', 'html');
res
..write('<!DOCTYPE html>')
..write('<html>')
@ -165,7 +165,7 @@ class VirtualDirectory {
List<FileSystemEntity> entities = await directory
.list(followLinks: false)
.toList()
.then((l) => new List.from(l));
.then((l) => List.from(l));
entities.sort((a, b) {
if (a is Directory) {
if (b is Directory) return a.path.compareTo(b.path);
@ -213,8 +213,8 @@ class VirtualDirectory {
(mimeType?.isNotEmpty == true && value?.contains(mimeType) == true) ||
value?.contains('*/*') == true;
if (!acceptable)
throw new AngelHttpException(
new UnsupportedError(
throw AngelHttpException(
UnsupportedError(
'Client requested $value, but server wanted to send $mimeType.'),
statusCode: 406,
message: '406 Not Acceptable');
@ -237,16 +237,16 @@ class VirtualDirectory {
res.headers['accept-ranges'] = 'bytes';
_ensureContentTypeAllowed(type, req);
res.headers['accept-ranges'] = 'bytes';
res.contentType = new MediaType.parse(type);
res.contentType = MediaType.parse(type);
if (useBuffer == true) res.useBuffer();
if (req.headers.value('range')?.startsWith('bytes=') != true) {
await res.streamFile(file);
} else {
var header = new RangeHeader.parse(req.headers.value('range'));
var header = RangeHeader.parse(req.headers.value('range'));
var items = RangeHeader.foldItems(header.items);
var totalFileSize = await file.length();
header = new RangeHeader(items);
header = RangeHeader(items);
for (var item in header.items) {
bool invalid = false;
@ -257,23 +257,23 @@ class VirtualDirectory {
invalid = item.end == -1;
if (invalid) {
throw new AngelHttpException(
new Exception("Semantically invalid, or unbounded range."),
throw AngelHttpException(
Exception("Semantically invalid, or unbounded range."),
statusCode: 416,
message: "Semantically invalid, or unbounded range.");
}
// Ensure it's within range.
if (item.start >= totalFileSize || item.end >= totalFileSize) {
throw new AngelHttpException(
new Exception("Given range $item is out of bounds."),
throw AngelHttpException(
Exception("Given range $item is out of bounds."),
statusCode: 416,
message: "Given range $item is out of bounds.");
}
}
if (header.items.isEmpty) {
throw new AngelHttpException(null,
throw AngelHttpException(null,
statusCode: 416, message: '`Range` header may not be empty.');
} else if (header.items.length == 1) {
var item = header.items[0];
@ -298,7 +298,7 @@ class VirtualDirectory {
}
}
res.contentType = new MediaType.parse(
res.contentType = MediaType.parse(
app.mimeTypeResolver.lookup(file.path) ??
'application/octet-stream');
res.statusCode = 206;
@ -307,7 +307,7 @@ class VirtualDirectory {
await stream.pipe(res);
return false;
} else {
var transformer = new RangeHeaderTransformer(
var transformer = RangeHeaderTransformer(
header,
app.mimeTypeResolver.lookup(file.path) ??
'application/octet-stream',
@ -315,7 +315,7 @@ class VirtualDirectory {
res.statusCode = 206;
res.headers['content-length'] =
transformer.computeContentLength(totalFileSize).toString();
res.contentType = new MediaType(
res.contentType = MediaType(
'multipart', 'byteranges', {'boundary': transformer.boundary});
await file.openRead().transform(transformer).pipe(res);
return false;

View file

@ -1,12 +1,12 @@
name: angel_static
description: Static server middleware for Angel. Also capable of serving Range responses.
environment:
sdk: ">=1.8.0 <3.0.0"
sdk: ">=2.0.0 <3.0.0"
homepage: https://github.com/angel-dart/static
author: Tobe O <thosakwe@gmail.com>
version: 2.1.2+1
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework: ^2.0.0-rc.0
convert: ^2.0.0
crypto: ^2.0.0
file: ^5.0.0
@ -18,4 +18,5 @@ dev_dependencies:
http:
logging: ^0.11.0
matcher: ^0.12.0
pedantic: ^1.0.0
test: ^1.0.0

View file

@ -12,22 +12,22 @@ main() {
AngelHttp http;
Directory testDir = const LocalFileSystem().directory('test');
String url;
Client client = new Client();
Client client = Client();
setUp(() async {
app = new Angel();
http = new AngelHttp(app);
app.logger = new Logger('angel')..onRecord.listen(print);
app = Angel();
http = AngelHttp(app);
app.logger = Logger('angel')..onRecord.listen(print);
app.fallback(
new VirtualDirectory(app, const LocalFileSystem(),
VirtualDirectory(app, const LocalFileSystem(),
source: testDir,
publicPath: '/virtual',
indexFileNames: ['index.txt']).handleRequest,
);
app.fallback(
new VirtualDirectory(app, const LocalFileSystem(),
VirtualDirectory(app, const LocalFileSystem(),
source: testDir,
useBuffer: true,
indexFileNames: ['index.php', 'index.txt']).handleRequest,

View file

@ -8,11 +8,11 @@ main() async {
Angel app;
AngelHttp http;
Directory testDir = const LocalFileSystem().directory('test');
app = new Angel();
http = new AngelHttp(app);
app = Angel();
http = AngelHttp(app);
app.fallback(
new CachingVirtualDirectory(app, const LocalFileSystem(),
CachingVirtualDirectory(app, const LocalFileSystem(),
source: testDir,
maxAge: 350,
onlyInProduction: false,

View file

@ -14,14 +14,14 @@ main() {
AngelHttp http;
Directory testDir = const LocalFileSystem().directory('test');
String url;
Client client = new Client();
Client client = Client();
setUp(() async {
app = new Angel();
http = new AngelHttp(app);
app = Angel();
http = AngelHttp(app);
app.fallback(
new CachingVirtualDirectory(app, const LocalFileSystem(),
CachingVirtualDirectory(app, const LocalFileSystem(),
source: testDir, maxAge: 350, onlyInProduction: false,
//publicPath: '/virtual',
indexFileNames: ['index.txt']).handleRequest,
@ -31,7 +31,7 @@ main() {
app.dumpTree(showMatchers: true);
app.logger = new Logger('angel_static')
app.logger = Logger('angel_static')
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
@ -63,7 +63,7 @@ main() {
test('if-modified-since', () async {
var response = await client.get("$url", headers: {
'if-modified-since':
HttpDate.format(new DateTime.now().add(new Duration(days: 365)))
HttpDate.format(DateTime.now().add(Duration(days: 365)))
});
print('Response status: ${response.statusCode}');

View file

@ -23,11 +23,11 @@ main() async {
.readAsString();
// Initialize app
var app = new Angel();
app.logger = new Logger('angel')..onRecord.listen(print);
var app = Angel();
app.logger = Logger('angel')..onRecord.listen(print);
app.fallback(
new VirtualDirectory(app, const LocalFileSystem(),
VirtualDirectory(app, const LocalFileSystem(),
source: swaggerUiDistDir, publicPath: 'swagger/')
.handleRequest,
);

View file

@ -11,7 +11,7 @@ main() {
TestClient client;
setUp(() async {
fileSystem = new MemoryFileSystem();
fileSystem = MemoryFileSystem();
var webDir = fileSystem.directory('web');
await webDir.create(recursive: true);
@ -19,9 +19,9 @@ main() {
var indexFile = webDir.childFile('index.html');
await indexFile.writeAsString('index');
app = new Angel();
app = Angel();
var vDir = new VirtualDirectory(
var vDir = VirtualDirectory(
app,
fileSystem,
source: webDir,
@ -32,7 +32,7 @@ main() {
..fallback(vDir.pushState('index.html'))
..fallback((req, res) => 'Fallback');
app.logger = new Logger('push_state')
app.logger = Logger('push_state')
..onRecord.listen(
(rec) {
print(rec);