1.1.2
This commit is contained in:
parent
4d4422d31f
commit
3ef26d9ada
5 changed files with 71 additions and 43 deletions
|
@ -1,7 +1,7 @@
|
||||||
# angel_static
|
# angel_static
|
||||||
|
|
||||||
![version 1.1.1](https://img.shields.io/badge/version-1.1.1-red.svg)
|
[![version 1.1.2](https://img.shields.io/badge/pub-1.1.2-brightgreen.svg)](https://pub.dartlang.org/packages/angel_static)
|
||||||
![build status](https://travis-ci.org/angel-dart/static.svg?branch=master)
|
[![build status](https://travis-ci.org/angel-dart/static.svg?branch=master)](https://travis-ci.org/angel-dart/static)
|
||||||
|
|
||||||
Static server middleware for Angel.
|
Static server middleware for Angel.
|
||||||
|
|
||||||
|
@ -10,8 +10,7 @@ In `pubspec.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.0.0-dev
|
angel_static: ^1.1.0
|
||||||
angel_static: ^1.1.0-dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
@ -41,3 +40,4 @@ The `VirtualDirectory` API accepts a few named parameters:
|
||||||
- **debug**: Print verbose debug output.
|
- **debug**: Print verbose debug output.
|
||||||
- **callback**: Runs before sending a file to a client. Use this to set headers, etc. If it returns anything other than `null` or `true`,
|
- **callback**: Runs before sending a file to a client. Use this to set headers, etc. If it returns anything other than `null` or `true`,
|
||||||
then the callback's result will be sent to the user, instead of the file contents.
|
then the callback's result will be sent to the user, instead of the file contents.
|
||||||
|
- **streamToIO**: If set to `true`, files will be streamed to `res.io`, instead of added to `res.buffer`.. Default is `false`.
|
|
@ -25,7 +25,7 @@ String _pathify(String path) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
class VirtualDirectory {
|
class VirtualDirectory implements AngelPlugin {
|
||||||
final bool debug;
|
final bool debug;
|
||||||
String _prefix;
|
String _prefix;
|
||||||
Directory _source;
|
Directory _source;
|
||||||
|
@ -34,12 +34,16 @@ class VirtualDirectory {
|
||||||
final List<String> indexFileNames;
|
final List<String> indexFileNames;
|
||||||
final String publicPath;
|
final String publicPath;
|
||||||
|
|
||||||
|
/// If set to `true`, files will be streamed to `res.io`, instead of added to `res.buffer`.
|
||||||
|
final bool streamToIO;
|
||||||
|
|
||||||
VirtualDirectory(
|
VirtualDirectory(
|
||||||
{Directory source,
|
{Directory source,
|
||||||
this.debug: false,
|
this.debug: false,
|
||||||
this.indexFileNames: const ['index.html'],
|
this.indexFileNames: const ['index.html'],
|
||||||
this.publicPath: '/',
|
this.publicPath: '/',
|
||||||
this.callback}) {
|
this.callback,
|
||||||
|
this.streamToIO: false}) {
|
||||||
_prefix = publicPath.replaceAll(_straySlashes, '');
|
_prefix = publicPath.replaceAll(_straySlashes, '');
|
||||||
|
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
|
@ -56,9 +60,46 @@ class VirtualDirectory {
|
||||||
if (debug) print(msg);
|
if (debug) print(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
call(AngelBase app) async => serve(app);
|
call(Angel app) async => serve(app);
|
||||||
|
|
||||||
Future<bool> sendFile(
|
void serve(Router router) {
|
||||||
|
_printDebug('Source directory: ${source.absolute.path}');
|
||||||
|
_printDebug('Public path prefix: "$_prefix"');
|
||||||
|
router.get('$publicPath/*',
|
||||||
|
(RequestContext req, ResponseContext res) async {
|
||||||
|
var path = req.path.replaceAll(_straySlashes, '');
|
||||||
|
return servePath(path, req, res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
servePath(String path, RequestContext req, ResponseContext res) async {
|
||||||
|
if (_prefix.isNotEmpty) {
|
||||||
|
path = path.replaceAll(new RegExp('^' + _pathify(_prefix)), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.isEmpty) path = '.';
|
||||||
|
|
||||||
|
var absolute = source.absolute.uri.resolve(path).toFilePath();
|
||||||
|
var stat = await FileStat.stat(absolute);
|
||||||
|
return await serveStat(absolute, stat, req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> serveStat(String absolute, FileStat stat, RequestContext req,
|
||||||
|
ResponseContext res) async {
|
||||||
|
if (stat.type == FileSystemEntityType.NOT_FOUND)
|
||||||
|
return true;
|
||||||
|
else if (stat.type == FileSystemEntityType.DIRECTORY)
|
||||||
|
return await serveDirectory(new Directory(absolute), req, res);
|
||||||
|
else if (stat.type == FileSystemEntityType.FILE)
|
||||||
|
return await serveFile(new File(absolute), req, res);
|
||||||
|
else if (stat.type == FileSystemEntityType.LINK) {
|
||||||
|
var link = new Link(absolute);
|
||||||
|
return await servePath(await link.resolveSymbolicLinks(), req, res);
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> serveFile(
|
||||||
File file, RequestContext req, ResponseContext res) async {
|
File file, RequestContext req, ResponseContext res) async {
|
||||||
_printDebug('Sending file ${file.absolute.path}...');
|
_printDebug('Sending file ${file.absolute.path}...');
|
||||||
_printDebug('MIME type for ${file.path}: ${lookupMimeType(file.path)}');
|
_printDebug('MIME type for ${file.path}: ${lookupMimeType(file.path)}');
|
||||||
|
@ -71,44 +112,24 @@ class VirtualDirectory {
|
||||||
}
|
}
|
||||||
|
|
||||||
res.headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path);
|
res.headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path);
|
||||||
|
|
||||||
|
if (streamToIO == true)
|
||||||
await res.streamFile(file);
|
await res.streamFile(file);
|
||||||
|
else
|
||||||
|
await res.sendFile(file);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serve(Router router) {
|
Future<bool> serveDirectory(
|
||||||
_printDebug('Source directory: ${source.absolute.path}');
|
Directory directory, RequestContext req, ResponseContext res) async {
|
||||||
_printDebug('Public path prefix: "$_prefix"');
|
|
||||||
router.get('$publicPath/*',
|
|
||||||
(RequestContext req, ResponseContext res) async {
|
|
||||||
var path = req.path.replaceAll(_straySlashes, '');
|
|
||||||
return serveFile(path, req, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
serveFile(String path, RequestContext req, ResponseContext res) async {
|
|
||||||
if (_prefix.isNotEmpty) {
|
|
||||||
path = path.replaceAll(new RegExp('^' + _pathify(_prefix)), '');
|
|
||||||
}
|
|
||||||
|
|
||||||
final file = new File.fromUri(source.absolute.uri.resolve(path));
|
|
||||||
_printDebug('Attempting to statically serve file: ${file.absolute.path}');
|
|
||||||
|
|
||||||
if (await file.exists()) {
|
|
||||||
return sendFile(file, req, res);
|
|
||||||
} else {
|
|
||||||
// Try to resolve index
|
|
||||||
if (path.isEmpty) {
|
|
||||||
for (String indexFileName in indexFileNames) {
|
for (String indexFileName in indexFileNames) {
|
||||||
final index =
|
final index =
|
||||||
new File.fromUri(source.absolute.uri.resolve(indexFileName));
|
new File.fromUri(directory.absolute.uri.resolve(indexFileName));
|
||||||
if (await index.exists()) {
|
if (await index.exists()) {
|
||||||
return await sendFile(index, req, res);
|
return await serveFile(index, req, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_printDebug('File "$path" does not exist, and is not an index.');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0"
|
||||||
homepage: https://github.com/angel-dart/angel_static
|
homepage: https://github.com/angel-dart/angel_static
|
||||||
author: thosakwe <thosakwe@gmail.com>
|
author: thosakwe <thosakwe@gmail.com>
|
||||||
version: 1.1.1
|
version: 1.1.2
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.0.0-dev
|
angel_framework: ^1.0.0-dev
|
||||||
mime: ^0.9.3
|
mime: ^0.9.3
|
||||||
|
|
|
@ -42,6 +42,12 @@ main() {
|
||||||
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('can serve child directories', () async {
|
||||||
|
var response = await client.get("$url/nested");
|
||||||
|
expect(response.body, equals("Bird"));
|
||||||
|
expect(response.headers[HttpHeaders.CONTENT_TYPE], contains("text/plain"));
|
||||||
|
});
|
||||||
|
|
||||||
test('non-existent files are skipped', () async {
|
test('non-existent files are skipped', () async {
|
||||||
var response = await client.get("$url/nonexist.ent");
|
var response = await client.get("$url/nonexist.ent");
|
||||||
expect(response.body, equals('"Fallback"'));
|
expect(response.body, equals('"Fallback"'));
|
||||||
|
|
1
test/nested/index.txt
Normal file
1
test/nested/index.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Bird
|
Loading…
Reference in a new issue