Skip to content

Commit

Permalink
Merge pull request #115 from dukefirehawk/bug-fix/72
Browse files Browse the repository at this point in the history
Bug fix/72
  • Loading branch information
dukefirehawk authored Nov 13, 2023
2 parents 46f5bfb + 94d1c5a commit a1c6584
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 125 deletions.
5 changes: 5 additions & 0 deletions packages/framework/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## 8.2.0

* Add `addResponseHeader` to `AngelHttp` to add headers to HTTP default response
* Add `removeResponseHeader` to `AngelHttp` to remove headers from HTTP default response

## 8.1.1

* Updated broken image on README.md
Expand Down
28 changes: 15 additions & 13 deletions packages/framework/lib/src/http/angel_http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
HttpRequestContext, HttpResponseContext> {
@override
Uri get uri {
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not intialised");
//}
return Uri(
scheme: 'http', host: server?.address.address, port: server?.port);
}
Expand All @@ -38,7 +35,7 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,

/// An instance mounted on a server started by the [serverGenerator].
factory AngelHttp.custom(Angel app, ServerGeneratorType serverGenerator,
{bool useZone = true}) {
{bool useZone = true, Map<String, String> headers = const {}}) {
return AngelHttp._(app, serverGenerator, useZone);
}

Expand Down Expand Up @@ -67,15 +64,6 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
return AngelHttp.fromSecurityContext(app, serverContext, useZone: useZone);
}

/// Use [server] instead.
//@deprecated
//HttpServer get httpServer {
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not initialised");
//}
// return server;
//}

Future handleRequest(HttpRequest request) =>
handleRawRequest(request, request.response);

Expand All @@ -88,6 +76,20 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
return await super.close();
}

/// Remove headers from HTTP Response
void removeResponseHeader(Map<String, Object> headers) {
headers.forEach((key, value) {
server?.defaultResponseHeaders.remove(key, value);
});
}

/// Add headers to HTTP Response
void addResponseHeader(Map<String, Object> headers) {
headers.forEach((key, value) {
server?.defaultResponseHeaders.add(key, value);
});
}

@override
Future closeResponse(HttpResponse response) => response.close();

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: angel3_framework
version: 8.1.1
version: 8.2.0
description: A high-powered HTTP server extensible framework with dependency injection, routing and much more.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/framework
Expand Down
2 changes: 2 additions & 0 deletions packages/framework/test/all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'service_map_test.dart' as service_map;
import 'services_test.dart' as services;
import 'streaming_test.dart' as streaming;
import 'view_generator_test.dart' as view_generator;
//import 'response_header_test.dart' as response_header;
import 'package:test/test.dart';

/// For running with coverage
Expand All @@ -36,6 +37,7 @@ void main() {
group('accepts', accepts.main);
group('anonymous service', anonymous_service.main);
group('body', body.main);
//group('response_header', response_header.main);
group('controller', controller.main);
group('detach', detach.main);
group('di', di.main);
Expand Down
58 changes: 58 additions & 0 deletions packages/framework/test/response_header_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'dart:io';

import 'package:angel3_container/mirrors.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/src/http/angel_http.dart';
import 'package:test/test.dart';

void main() {
late Angel app;
late AngelHttp http;
late HttpClient client;

setUp(() async {
app = Angel(reflector: MirrorsReflector());
http = AngelHttp(app);

await http.startServer();

var formData = {'id': 100, 'name': 'William'};
app.get('/api/v1/user/list', (RequestContext req, res) async {
//await req.parseBody();
//res.write('Hello, World!');
res.json(formData);
});

client = HttpClient();
});

tearDown(() async {
client.close();
await http.close();
});

test('Remove Response Header', () async {
http.removeResponseHeader({'x-frame-options': 'SAMEORIGIN'});

var request = await client.get('localhost', 3000, '/api/v1/user/list');
HttpClientResponse response = await request.close();
//print(response.headers);
expect(response.headers['x-frame-options'], isNull);
}, skip: true);

test('Add Response Header', () async {
http.addResponseHeader({
'X-XSRF_TOKEN':
'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'
});

var request = await client.get('localhost', 3000, '/api/v1/user/list');
HttpClientResponse response = await request.close();
//print(response.headers);
expect(
response.headers['X-XSRF_TOKEN'],
equals([
'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'
]));
}, skip: true);
}
5 changes: 5 additions & 0 deletions packages/hot/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## 8.1.0

* Updated `vm_service` to 13.0.0
* Added configurable HTTP response header

## 8.0.0

* Require Dart >= 3.0
Expand Down
8 changes: 2 additions & 6 deletions packages/hot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

Supports *hot reloading* of Angel3 servers on file changes. This is faster and more reliable than merely reactively restarting a `Process`. This package only works with the [Angel3 framework](https://pub.dev/packages/angel3_framework).

**Not recommended to use in production, unless you are specifically intending for a "hot code push" in production..**
**Not recommended to use in production, unless you are specifically intending for a "hot code push" in production.**

## Installation

Expand All @@ -23,11 +23,7 @@ dependencies:
## Usage
This package is dependent on the Dart VM service, so you *must* run Dart with the `--observe` (or `--enable-vm-service`) argument!!!

Usage is fairly simple. Pass a function that creates an `Angel` server, along with a collection of paths to watch, to the `HotReloader` constructor. The rest is history!!!

The recommended pattern is to only use hot-reloading in your application entry point. Create your `Angel` instance within a separate function, conventionally named `createServer`.
This package is dependent on the Dart VM service, so you *must* run Dart with the `--observe` (or `--enable-vm-service`) argument. Usage is fairly simple. Pass a function that creates an `Angel` server, along with a collection of paths to watch, to the `HotReloader` constructor. The recommended pattern is to only use hot-reloading in your application entry point. Create your `Angel` instance within a separate function, conventionally named `createServer`.

You can watch:

Expand Down
79 changes: 46 additions & 33 deletions packages/hot/lib/angel3_hot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@ import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:belatuk_html_builder/elements.dart';
import 'package:io/ansi.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:vm_service/vm_service.dart' as vm;
import 'package:vm_service/vm_service_io.dart' as vm;
import 'package:watcher/watcher.dart';

/// A utility class that watches the filesystem for changes, and starts new instances of an Angel server.
class HotReloader {
late vm.VmService _client;
vm.IsolateRef? _mainIsolate;
final StreamController<WatchEvent> _onChange =
StreamController<WatchEvent>.broadcast();
final List _paths = [];
final StringRenderer _renderer = StringRenderer(pretty: false);
final Queue<HttpRequest> _requestQueue = Queue<HttpRequest>();
late HttpServer _io;
AngelHttp? _server;
Duration? _timeout;
late Duration _timeout;
late vm.VmService _client;
vm.VM? _vmachine;
vm.IsolateRef? _mainIsolate;

/// If `true` (default), then developers can `press 'r' to reload` the application on-the-fly.
///
Expand All @@ -47,7 +48,7 @@ class HotReloader {
///
/// If the timeout expires, then the request will be immediately terminated with a `502 Bad Gateway` error.
/// Default: `5s`
Duration? get timeout => _timeout;
Duration get timeout => _timeout;

/// The Dart VM service host.
///
Expand Down Expand Up @@ -116,20 +117,22 @@ class HotReloader {
Future handleRequest(HttpRequest request) async {
if (_server != null) {
return await _handle(request);
} else if (timeout == null) {
_requestQueue.add(request);
//} else if (timeout == null) {
// _requestQueue.add(request);
} else {
_requestQueue.add(request);
Timer(timeout!, () {
Timer(timeout, () {
if (_requestQueue.remove(request)) {
// Send 502 response
sendError(request, HttpStatus.badGateway, '502 Bad Gateway',
'Request timed out after ${timeout!.inMilliseconds}ms.');
'Request timed out after ${timeout.inMilliseconds}ms.');
}
});
}
}

Logger? get _appLogger => _server?.app.logger;

Future<AngelHttp> _generateServer() async {
var s = await generator();
await Future.forEach(s.startupHooks, s.configure);
Expand All @@ -138,23 +141,24 @@ class HotReloader {
}

void _logWarning(String msg) {
if (_server?.app.logger != null) {
_server?.app.logger.warning(msg);
if (_appLogger != null) {
_appLogger?.warning(msg);
} else {
print(yellow.wrap('WARNING: $msg'));
}
}

void _logInfo(String msg) {
if (_server?.app.logger != null) {
_server?.app.logger.info(msg);
if (_appLogger != null) {
_appLogger?.info(msg);
} else {
print(lightGray.wrap(msg));
}
}

/// Starts listening to requests and filesystem events.
Future<HttpServer> startServer([address, int? port]) async {
Future<HttpServer> startServer(
[String address = '127.0.0.1', int port = 3000]) async {
var isHot = true;
_server = await _generateServer();

Expand Down Expand Up @@ -185,9 +189,10 @@ class HotReloader {
_mainIsolate ??= _vmachine?.isolates?.first;

if (_vmachine != null) {
for (var isolate in _vmachine!.isolates ?? <vm.IsolateRef>[]) {
if (isolate.id != null) {
await _client.setIsolatePauseMode(isolate.id!,
for (var isolate in _vmachine?.isolates ?? <vm.IsolateRef>[]) {
var isolateId = isolate.id;
if (isolateId != null) {
await _client.setIsolatePauseMode(isolateId,
exceptionPauseMode: 'None');
}
}
Expand All @@ -203,7 +208,8 @@ class HotReloader {
while (_requestQueue.isNotEmpty) {
await _handle(_requestQueue.removeFirst());
}
var server = _io = await HttpServer.bind(address ?? '127.0.0.1', port ?? 0);
var server = _io = await HttpServer.bind(address, port);
//server.defaultResponseHeaders();
server.listen(handleRequest);

// Print a Flutter-like prompt...
Expand All @@ -213,7 +219,7 @@ class HotReloader {

Uri? observatoryUri;
if (isHot) {
observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri!);
observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri);
}

print(styleBold.wrap(
Expand Down Expand Up @@ -260,7 +266,7 @@ class HotReloader {
if (ch == $R) {
_logInfo('Manually restarting server...\n');
await _killServer();
await _server!.close();
await _server?.close();
var addr = _io.address.address;
var port = _io.port;
await _io.close(force: true);
Expand Down Expand Up @@ -359,21 +365,23 @@ class HotReloader {
scheduleMicrotask(() async {
// Disconnect active WebSockets
try {
var ws = _server!.app.container.make<AngelWebSocket>();

for (var client in ws.clients) {
try {
await client.close();
} catch (e) {
_logWarning(
'Couldn\'t close WebSocket from session #${client.request.session!.id}: $e');
var ws = _server?.app.container.make<AngelWebSocket>();

if (ws != null) {
for (var client in ws.clients) {
try {
await client.close();
} catch (e) {
_logWarning(
'Couldn\'t close WebSocket from session #${client.request.session?.id}: $e');
}
}
}

// await Future.forEach(
// _server.app.shutdownHooks, _server.app.configure);
await _server!.app.close();
_server!.app.logger.clearListeners();
await _server?.app.close();
_server?.app.logger.clearListeners();
} catch (_) {
// Fail silently...
}
Expand All @@ -387,11 +395,16 @@ class HotReloader {
_server = null;

if (hot) {
var report = await _client.reloadSources(_mainIsolate!.id!);
var mainIsolateId = _mainIsolate?.id;
if (mainIsolateId != null) {
var report = await _client.reloadSources(mainIsolateId);

if (report.success != null) {
_logWarning(
'Hot reload failed - perhaps some sources have not been generated yet.');
if (report.success != null) {
_logWarning(
'Hot reload failed - perhaps some sources have not been generated yet.');
}
} else {
_logWarning('Hot reload failed - isolate id does not exist.');
}
}

Expand Down
Loading

0 comments on commit a1c6584

Please sign in to comment.