Fixed compatibility issues with 1.1.3

This commit is contained in:
Tobe O 2018-06-07 12:11:03 -04:00
parent 4639efd67f
commit 0c3936d602
10 changed files with 128 additions and 95 deletions

View file

@ -2,6 +2,7 @@
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/build" />

View file

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
<option name="VMOptions" value="--observe" />
<option name="filePath" value="$PROJECT_DIR$/example/basic/main.dart" />
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
<method />
</configuration>
</component>

View file

@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart (No VM service)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
<option name="filePath" value="$PROJECT_DIR$/example/basic/main.dart" />
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
<method />
</configuration>
</component>

View file

@ -1,8 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="server.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
<option name="VMOptions" value="--observe" />
<option name="filePath" value="$PROJECT_DIR$/example/basic/server.dart" />
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
<method />
</configuration>
</component>

View file

@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="server.dart (No VM service)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
<option name="filePath" value="$PROJECT_DIR$/example/basic/server.dart" />
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
<method />
</configuration>
</component>

4
CHANGELOG.md Normal file
View file

@ -0,0 +1,4 @@
# 1.1.1
* Disable the observatory from pausing the isolate
on exceptions, because Angel already handles
all exceptions by itself.

View file

@ -1,8 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:io' show Directory;
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_hot/angel_hot.dart';
import 'package:dart2_constant/convert.dart';
import 'package:dart2_constant/io.dart';
import 'package:logging/logging.dart';
import 'src/foo.dart';
@ -10,19 +11,19 @@ main() async {
var hot = new HotReloader(createServer, [
new Directory('src'),
new Directory('src'),
'server.dart',
'main.dart',
Uri.parse('package:angel_hot/angel_hot.dart')
]);
var server = await hot.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
print(
'Hot server listening at http://${server.address.address}:${server.port}');
var server = await hot.startServer('127.0.0.1', 3000);
print('Hot server listening at http://${server.address.address}:${server
.port}');
}
Future<Angel> createServer() async {
var app = new Angel();
app.lazyParseBodies = true;
app.injectSerializer(JSON.encode);
app.serializer = json.encode;
// Edit this line, and then refresh the page in your browser!
app.get('/', {'hello': 'hot world!'});
@ -31,8 +32,8 @@ Future<Angel> createServer() async {
app.use(() => throw new AngelHttpException.notFound());
app.injectEncoders({
'gzip': GZIP.encoder,
'deflate': ZLIB.encoder,
'gzip': gzip.encoder,
'deflate': zlib.encoder,
});
app.logger = new Logger('angel')

View file

@ -1,10 +1,25 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:io'
show
ContentType,
Directory,
File,
FileStat,
FileSystemEntity,
FileSystemException,
HttpHeaders,
HttpRequest,
HttpServer,
Link,
Platform,
exit,
stderr;
import 'dart:isolate';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_websocket/server.dart';
import 'package:dart2_constant/convert.dart';
import 'package:dart2_constant/io.dart';
import 'package:glob/glob.dart';
import 'package:html_builder/elements.dart';
import 'package:html_builder/html_builder.dart';
@ -15,13 +30,15 @@ import 'package:watcher/watcher.dart';
/// A utility class that watches the filesystem for changes, and starts new instances of an Angel server.
class HotReloader {
vm.VmService _client;
vm.IsolateRef _mainIsolate;
final StreamController<WatchEvent> _onChange =
new StreamController<WatchEvent>.broadcast();
final List _paths = [];
final StringRenderer _renderer = new StringRenderer(pretty: false);
final Queue<HttpRequest> _requestQueue = new Queue<HttpRequest>();
Angel _server;
AngelHttp _server;
Duration _timeout;
vm.VM _vmachine;
/// Invoked to load a new instance of [Angel] on file changes.
final FutureOr<Angel> Function() generator;
@ -62,33 +79,20 @@ class HotReloader {
_onChange.close();
}
Future handleRequest(HttpRequest request) async {
if (_server != null)
return await _server.handleRequest(request);
else if (timeout == null)
_requestQueue.add(request);
else {
_requestQueue.add(request);
new Timer(timeout, () {
if (_requestQueue.remove(request)) {
// Send 502 response
void sendError(HttpRequest request, int status, String title_, e) {
var doc = html(lang: 'en', c: [
head(c: [
meta(
name: 'viewport',
content: 'width=device-width, initial-scale=1'),
title(c: [text('502 Bad Gateway')])
meta(name: 'viewport', content: 'width=device-width, initial-scale=1'),
title(c: [text(title_)])
]),
body(c: [
h1(c: [text('502 Bad Gateway')]),
i(c: [
text('Request timed out after ${timeout.inMilliseconds}ms.')
])
h1(c: [text(title_)]),
i(c: [text(e.toString())])
])
]);
var response = request.response;
response.statusCode = HttpStatus.BAD_GATEWAY;
response.statusCode = HttpStatus.badGateway;
response.headers
..contentType = ContentType.HTML
..set(HttpHeaders.SERVER, 'angel_hot');
@ -100,20 +104,38 @@ class HotReloader {
true) {
response
..headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip')
..add(GZIP.encode(UTF8.encode(_renderer.render(doc))));
..add(gzip.encode(utf8.encode(_renderer.render(doc))));
} else
response.write(_renderer.render(doc));
response.close();
}
Future _handle(HttpRequest request) {
return _server.handleRequest(request);
}
Future handleRequest(HttpRequest request) async {
if (_server != null)
return await _handle(request);
else if (timeout == null)
_requestQueue.add(request);
else {
_requestQueue.add(request);
new Timer(timeout, () {
if (_requestQueue.remove(request)) {
// Send 502 response
sendError(request, HttpStatus.badGateway, '502 Bad Gateway',
'Request timed out after ${timeout.inMilliseconds}ms.');
}
});
}
}
Future<Angel> _generateServer() async {
var s = await generator() as Angel;
Future<AngelHttp> _generateServer() async {
var s = await generator();
await Future.forEach(s.startupHooks, s.configure);
s.optimizeForProduction();
return s;
return new AngelHttp(s);
}
/// Starts listening to requests and filesystem events.
@ -122,17 +144,21 @@ class HotReloader {
print(
'WARNING: You have instantiated a HotReloader without providing any filesystem paths to watch.');
var s = _server = await _generateServer();
while (!_requestQueue.isEmpty)
await s.handleRequest(_requestQueue.removeFirst());
_client = await vm.vmServiceConnect(
vmServiceHost ?? 'localhost', vmServicePort ?? 8181);
_vmachine ??= await _client.getVM();
_mainIsolate ??= _vmachine.isolates.first;
await _client.setExceptionPauseMode(_mainIsolate.id, 'None');
_server = await _generateServer();
while (!_requestQueue.isEmpty) await _handle(_requestQueue.removeFirst());
_onChange.stream
.transform(new _Debounce(new Duration(seconds: 1)))
.listen(_handleWatchEvent);
await _listenToFilesystem();
var server = await HttpServer.bind(
address ?? InternetAddress.LOOPBACK_IP_V4, port ?? 0);
var server = await HttpServer.bind(address ?? '127.0.0.1', port ?? 0);
server.listen(handleRequest);
return server;
}
@ -174,14 +200,14 @@ class HotReloader {
_listen() async {
try {
var stat = await FileStat.stat(path);
if (stat.type == FileSystemEntityType.LINK) {
if (stat.type == FileSystemEntityType.link) {
var lnk = new Link(path);
var p = await lnk.resolveSymbolicLinks();
return await _listenToStat(p);
} else if (stat.type == FileSystemEntityType.FILE) {
} else if (stat.type == FileSystemEntityType.file) {
var file = new File(path);
if (!await file.exists()) return null;
} else if (stat.type == FileSystemEntityType.DIRECTORY) {
} else if (stat.type == FileSystemEntityType.directory) {
var dir = new Directory(path);
if (!await dir.exists()) return null;
} else
@ -204,7 +230,8 @@ class HotReloader {
if (r == null) {
print(
'WARNING: Unable to watch path "$path" from working directory "${Directory.current.path}". Please ensure that it exists.');
'WARNING: Unable to watch path "$path" from working directory "${Directory
.current.path}". Please ensure that it exists.');
}
}
@ -216,27 +243,24 @@ class HotReloader {
// Do this asynchronously, because we really don't care about the old server anymore.
new Future(() async {
// Disconnect active WebSockets
var ws = old.container.make(AngelWebSocket) as AngelWebSocket;
var ws = old.app.container.make(AngelWebSocket) as AngelWebSocket;
for (var client in ws.clients) {
try {
await client.close(WebSocketStatus.GOING_AWAY);
await client.close(WebSocketStatus.goingAway);
} catch (e) {
stderr.writeln(
'Couldn\'t close WebSocket from session #${client.request.session.id}: $e');
'Couldn\'t close WebSocket from session #${client.request
.session.id}: $e');
}
}
Future.forEach(old.shutdownHooks, old.configure);
Future.forEach(old.app.shutdownHooks, old.app.configure);
});
}
_server = null;
_client ??= await vm.vmServiceConnect(
vmServiceHost ?? 'localhost', vmServicePort ?? 8181);
var vmachine = await _client.getVM();
var mainIsolate = vmachine.isolates.first;
var report = await _client.reloadSources(mainIsolate.id);
var report = await _client.reloadSources(_mainIsolate.id);
if (!report.success) {
stderr.writeln('Hot reload failed!!!');
@ -246,12 +270,11 @@ class HotReloader {
var s = await _generateServer();
_server = s;
while (!_requestQueue.isEmpty)
await s.handleRequest(_requestQueue.removeFirst());
while (!_requestQueue.isEmpty) await _handle(_requestQueue.removeFirst());
}
}
class _Debounce<S> implements StreamTransformer<S, S> {
class _Debounce<S> extends StreamTransformerBase<S, S> {
final Duration _delay;
const _Debounce(this._delay);

View file

@ -1,16 +1,20 @@
name: angel_hot
description: Supports hot reloading of Angel servers on file changes.
version: 1.1.0
version: 1.1.1
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/hot
environment:
sdk: ">=1.19.0"
sdk: ">=1.19.0 <3.0.0"
dependencies:
angel_framework: ^1.1.0-alpha
angel_framework: ^1.1.0
angel_websocket: ^1.1.0-alpha
dart2_constant: ^1.0.0
glob: ^1.0.0
html_builder: ^1.0.0
vm_service_lib: 0.3.5
vm_service_lib: ^0.3.5
watcher: ^0.9.0
dev_dependencies:
angel_test: ^1.0.0
http: ^0.11.3
logging: ^0.11.0
test: ^0.12.15