This commit is contained in:
Tobe O 2018-11-13 16:25:17 -05:00
parent 4f642cec90
commit 9f63ef57d9
10 changed files with 64 additions and 38 deletions

View file

@ -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`.

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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<bool> handleRequest(RequestContext req, ResponseContext res) {
if (req.method != 'GET') return new Future<bool>.value(true);
if (req.method != 'GET' && req.method != 'HEAD')
return new Future<bool>.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('<!DOCTYPE html>')
@ -216,7 +223,7 @@ class VirtualDirectory {
/// Writes the contents of a file to a response.
Future<bool> 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;
}
}

View file

@ -4,17 +4,15 @@ environment:
sdk: ">=1.8.0 <3.0.0"
homepage: https://github.com/angel-dart/static
author: Tobe O <thosakwe@gmail.com>
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

2
test/HELLO.md Normal file
View file

@ -0,0 +1,2 @@
# hello
world!

View file

@ -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 {

View file

@ -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';

View file

@ -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 {