From 9f63ef57d9229de8eb073870890c0ac6c96284a3 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Tue, 13 Nov 2018 16:25:17 -0500 Subject: [PATCH] 2.0.1 --- CHANGELOG.md | 5 +++++ README.md | 9 +++++---- example/main.dart | 28 ++++++++++++++++++++++------ lib/src/cache.dart | 20 +++++++++----------- lib/src/virtual_directory.dart | 25 ++++++++++++++----------- pubspec.yaml | 6 ++---- test/HELLO.md | 2 ++ test/all_test.dart | 3 ++- test/cache_sample.dart | 1 + test/cache_test.dart | 3 ++- 10 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 test/HELLO.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 66600ff0..f3dfc388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2.0.1 +* Remove use of `sendFile`. +* Add a `p.isWithin` check to ensure that paths do not escape the `source` directory. +* Handle `HEAD` requests. + # 2.0.0 * Upgrade dependencies to Angel 2 + file@5. * Replace `useStream` with `useBuffer`. diff --git a/README.md b/README.md index 760b9773..be8aba6b 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Keep in mind that `angel_static` uses `package:file` instead of `dart:io`. ```dart import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; import 'package:angel_static/angel_static.dart'; import 'package:file/local.dart'; @@ -32,10 +33,10 @@ main() async { var vDir = new CachingVirtualDirectory(app, fs, source: new Directory('./public')); // Mount the VirtualDirectory's request handler - app.use(vDir.handleRequest); + app.fallback(vDir.handleRequest); // Start your server!!! - await app.startServer(); + await new AngelHttp(app).startServer(); } ``` @@ -49,10 +50,10 @@ the user is requesting that file. This can be very useful for SPA's. var vDir = new CachingVirtualDirectory(...); // Mount it -app.use(vDir.handleRequest); +app.fallback(vDir.handleRequest); // Fallback to index.html on 404 -app.use(vDir.pushState('index.html')); +app.fallback(vDir.pushState('index.html')); ``` # Options diff --git a/example/main.dart b/example/main.dart index 4bbee909..ca11c5b8 100644 --- a/example/main.dart +++ b/example/main.dart @@ -1,17 +1,33 @@ import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; import 'package:angel_static/angel_static.dart'; import 'package:file/local.dart'; +import 'package:logging/logging.dart'; main() async { var app = new Angel(); var http = new AngelHttp(app); var fs = const LocalFileSystem(); - var vDir = new VirtualDirectory( - app, - fs, - allowDirectoryListing: true, - source: fs.directory(fs.currentDirectory), - ); + var vDir = new CachingVirtualDirectory(app, fs, + allowDirectoryListing: true, + source: fs.currentDirectory, + maxAge: const Duration(days: 24).inSeconds); + + app.mimeTypeResolver + ..addExtension('', 'text/plain') + ..addExtension('dart', 'text/dart') + ..addExtension('lock', 'text/plain') + ..addExtension('markdown', 'text/plain') + ..addExtension('md', 'text/plain') + ..addExtension('yaml', 'text/plain'); + + app.logger = new Logger('example') + ..onRecord.listen((rec) { + print(rec); + if (rec.error != null) print(rec.error); + if (rec.stackTrace != null) print(rec.stackTrace); + }); + app.fallback(vDir.handleRequest); var server = await http.startServer('127.0.0.1', 3000); diff --git a/lib/src/cache.dart b/lib/src/cache.dart index c28f5b47..01cb4548 100644 --- a/lib/src/cache.dart +++ b/lib/src/cache.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'dart:io' show HttpDate; import 'package:angel_framework/angel_framework.dart'; import 'package:file/file.dart'; -//import 'package:intl/intl.dart'; -import 'package:mime/mime.dart'; import 'virtual_directory.dart'; /// Generates a weak ETag from the given buffer. @@ -85,21 +83,21 @@ class CachingVirtualDirectory extends VirtualDirectory { return super.serveFile(file, stat, req, res); } else { if (useEtags == true) { - var etags = req.headers['if-none-match']; + var etagsToMatchAgainst = req.headers['if-none-match']; - if (etags?.isNotEmpty == true) { + if (etagsToMatchAgainst?.isNotEmpty == true) { bool hasBeenModified = false; - for (var etag in etags) { + for (var etag in etagsToMatchAgainst) { if (etag == '*') hasBeenModified = true; else { - hasBeenModified = _etags.containsKey(file.absolute.path) && - _etags[file.absolute.path] == etag; + hasBeenModified = !_etags.containsKey(file.absolute.path) || + _etags[file.absolute.path] != etag; } } - if (hasBeenModified) { + if (!hasBeenModified) { res.statusCode = 304; setCachedHeaders(stat.modified, req, res); return new Future.value(false); @@ -128,11 +126,11 @@ class CachingVirtualDirectory extends VirtualDirectory { return file.readAsBytes().then((buf) { var etag = _etags[file.absolute.path] = weakEtag(buf); - res.statusCode = 200; + //res.statusCode = 200; res.headers ..['ETag'] = etag - ..['content-type'] = - lookupMimeType(file.path) ?? 'application/octet-stream'; + ..['content-type'] = res.app.mimeTypeResolver.lookup(file.path) ?? + 'application/octet-stream'; setCachedHeaders(stat.modified, req, res); res.add(buf); return false; diff --git a/lib/src/virtual_directory.dart b/lib/src/virtual_directory.dart index fc90fe31..4060f6cc 100644 --- a/lib/src/virtual_directory.dart +++ b/lib/src/virtual_directory.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; import 'package:file/file.dart'; import 'package:http_parser/http_parser.dart'; -import 'package:mime/mime.dart'; import 'package:path/path.dart' as p; final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?'); @@ -70,7 +69,8 @@ class VirtualDirectory { /// Responds to incoming HTTP requests. Future handleRequest(RequestContext req, ResponseContext res) { - if (req.method != 'GET') return new Future.value(true); + if (req.method != 'GET' && req.method != 'HEAD') + return new Future.value(true); var path = req.path.replaceAll(_straySlashes, ''); if (_prefix?.isNotEmpty == true && !path.startsWith(_prefix)) @@ -114,6 +114,11 @@ class VirtualDirectory { path = path.replaceAll(_straySlashes, ''); var absolute = source.absolute.uri.resolve(path).toFilePath(); + var parent = source.absolute.uri.toFilePath(); + + if (!p.isWithin(parent, absolute) && !p.equals(parent, absolute)) + return true; + var stat = await fileSystem.stat(absolute); return await serveStat(absolute, path, stat, req, res); } @@ -140,11 +145,13 @@ class VirtualDirectory { final index = fileSystem.file(directory.absolute.uri.resolve(indexFileName)); if (await index.exists()) { + if (req.method == 'HEAD') return false; return await serveFile(index, stat, req, res); } } if (allowDirectoryListing == true) { + if (req.method == 'HEAD') return false; res.contentType = new MediaType('text', 'html'); res ..write('') @@ -216,7 +223,7 @@ class VirtualDirectory { /// Writes the contents of a file to a response. Future serveFile( File file, FileStat stat, RequestContext req, ResponseContext res) async { - res.statusCode = 200; + if (req.method == 'HEAD') return false; if (callback != null) { var r = callback(file, req, res); @@ -225,17 +232,13 @@ class VirtualDirectory { //if (r != null && r != true) return r; } - var type = lookupMimeType(file.path) ?? 'application/octet-stream'; + var type = + app.mimeTypeResolver.lookup(file.path) ?? 'application/octet-stream'; _ensureContentTypeAllowed(type, req); res.contentType = new MediaType.parse(type); - if (useBuffer == true) { - res.useBuffer(); - await res.sendFile(file); - } else { - await res.streamFile(file); - } - + if (useBuffer == true) res.useBuffer(); + await res.streamFile(file); return false; } } diff --git a/pubspec.yaml b/pubspec.yaml index cdbe8a8e..37fc94a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,17 +4,15 @@ environment: sdk: ">=1.8.0 <3.0.0" homepage: https://github.com/angel-dart/static author: Tobe O -version: 2.0.0 +version: 2.0.1 dependencies: angel_framework: ^2.0.0-alpha file: ^5.0.0 http_parser: ^3.0.0 - mime: ^0.9.3 path: ^1.4.2 dev_dependencies: angel_test: ^2.0.0-alpha - http: ^0.11.3 + http: logging: ^0.11.0 matcher: ^0.12.0 - mustache4dart: ^3.0.0-dev.0.0 test: ^1.0.0 diff --git a/test/HELLO.md b/test/HELLO.md new file mode 100644 index 00000000..de89e4df --- /dev/null +++ b/test/HELLO.md @@ -0,0 +1,2 @@ +# hello +world! \ No newline at end of file diff --git a/test/all_test.dart b/test/all_test.dart index 971ab023..b7526785 100644 --- a/test/all_test.dart +++ b/test/all_test.dart @@ -1,4 +1,5 @@ import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; import 'package:angel_static/angel_static.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; @@ -41,7 +42,7 @@ main() { }); tearDown(() async { - if (http.httpServer != null) await http.httpServer.close(force: true); + if (http.server != null) await http.server.close(force: true); }); test('can serve files, with correct Content-Type', () async { diff --git a/test/cache_sample.dart b/test/cache_sample.dart index 444054fe..aaab4012 100644 --- a/test/cache_sample.dart +++ b/test/cache_sample.dart @@ -1,4 +1,5 @@ import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; import 'package:angel_static/angel_static.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; diff --git a/test/cache_test.dart b/test/cache_test.dart index c767cb58..83ebce65 100644 --- a/test/cache_test.dart +++ b/test/cache_test.dart @@ -1,5 +1,6 @@ import 'dart:io' show HttpDate; import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; import 'package:angel_static/angel_static.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; @@ -42,7 +43,7 @@ main() { }); tearDown(() async { - if (http.httpServer != null) await http.httpServer.close(force: true); + if (http.server != null) await http.server.close(force: true); }); test('sets etag, cache-control, expires, last-modified', () async {