2.1.3
This commit is contained in:
parent
42779a85e4
commit
fd53da891f
12 changed files with 85 additions and 73 deletions
|
@ -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.
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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}');
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}');
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue