Updated framework and production

This commit is contained in:
thomashii@dukefirehawk.com 2023-11-13 08:59:18 +08:00
parent 46f5bfb600
commit 26c39effec
15 changed files with 218 additions and 124 deletions

View file

@ -1,5 +1,10 @@
# Change Log # Change Log
## 8.2.0
* Add `responseHeader` to `AngelHttp` to add headers to HTTP default response
* Add `removeResponseHeader` to `AngelHttp` to remove headers from HTTP default response
## 8.1.1 ## 8.1.1
* Updated broken image on README.md * Updated broken image on README.md

View file

@ -22,9 +22,6 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
HttpRequestContext, HttpResponseContext> { HttpRequestContext, HttpResponseContext> {
@override @override
Uri get uri { Uri get uri {
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not intialised");
//}
return Uri( return Uri(
scheme: 'http', host: server?.address.address, port: server?.port); scheme: 'http', host: server?.address.address, port: server?.port);
} }
@ -38,7 +35,7 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
/// An instance mounted on a server started by the [serverGenerator]. /// An instance mounted on a server started by the [serverGenerator].
factory AngelHttp.custom(Angel app, ServerGeneratorType serverGenerator, factory AngelHttp.custom(Angel app, ServerGeneratorType serverGenerator,
{bool useZone = true}) { {bool useZone = true, Map<String, String> headers = const {}}) {
return AngelHttp._(app, serverGenerator, useZone); return AngelHttp._(app, serverGenerator, useZone);
} }
@ -67,15 +64,6 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
return AngelHttp.fromSecurityContext(app, serverContext, useZone: useZone); 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) => Future handleRequest(HttpRequest request) =>
handleRawRequest(request, request.response); handleRawRequest(request, request.response);
@ -88,6 +76,20 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
return await super.close(); 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 responseHeader(Map<String, Object> headers) {
headers.forEach((key, value) {
server?.defaultResponseHeaders.add(key, value);
});
}
@override @override
Future closeResponse(HttpResponse response) => response.close(); Future closeResponse(HttpResponse response) => response.close();

View file

@ -1,5 +1,5 @@
name: angel3_framework 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. description: A high-powered HTTP server extensible framework with dependency injection, routing and much more.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/framework repository: https://github.com/dukefirehawk/angel/tree/master/packages/framework

View file

@ -27,6 +27,7 @@ import 'service_map_test.dart' as service_map;
import 'services_test.dart' as services; import 'services_test.dart' as services;
import 'streaming_test.dart' as streaming; import 'streaming_test.dart' as streaming;
import 'view_generator_test.dart' as view_generator; import 'view_generator_test.dart' as view_generator;
//import 'response_header_test.dart' as response_header;
import 'package:test/test.dart'; import 'package:test/test.dart';
/// For running with coverage /// For running with coverage
@ -36,6 +37,7 @@ void main() {
group('accepts', accepts.main); group('accepts', accepts.main);
group('anonymous service', anonymous_service.main); group('anonymous service', anonymous_service.main);
group('body', body.main); group('body', body.main);
//group('response_header', response_header.main);
group('controller', controller.main); group('controller', controller.main);
group('detach', detach.main); group('detach', detach.main);
group('di', di.main); group('di', di.main);

View file

@ -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.responseHeader({
'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);
}

View file

@ -1,5 +1,10 @@
# Change Log # Change Log
## 8.1.0
* Updated `vm_service` to 13.0.0
* Added configurable HTTP response header
## 8.0.0 ## 8.0.0
* Require Dart >= 3.0 * Require Dart >= 3.0

View file

@ -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). 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 ## Installation
@ -23,11 +23,7 @@ dependencies:
## Usage ## Usage
This package is dependent on the Dart VM service, so you *must* run Dart with the `--observe` (or `--enable-vm-service`) argument!!! 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`.
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`.
You can watch: You can watch:

View file

@ -12,6 +12,7 @@ import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart'; import 'package:glob/list_local_fs.dart';
import 'package:belatuk_html_builder/elements.dart'; import 'package:belatuk_html_builder/elements.dart';
import 'package:io/ansi.dart'; import 'package:io/ansi.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:vm_service/vm_service.dart' as vm; import 'package:vm_service/vm_service.dart' as vm;
import 'package:vm_service/vm_service_io.dart' as vm; import 'package:vm_service/vm_service_io.dart' as vm;
@ -19,8 +20,6 @@ import 'package:watcher/watcher.dart';
/// A utility class that watches the filesystem for changes, and starts new instances of an Angel server. /// A utility class that watches the filesystem for changes, and starts new instances of an Angel server.
class HotReloader { class HotReloader {
late vm.VmService _client;
vm.IsolateRef? _mainIsolate;
final StreamController<WatchEvent> _onChange = final StreamController<WatchEvent> _onChange =
StreamController<WatchEvent>.broadcast(); StreamController<WatchEvent>.broadcast();
final List _paths = []; final List _paths = [];
@ -28,8 +27,10 @@ class HotReloader {
final Queue<HttpRequest> _requestQueue = Queue<HttpRequest>(); final Queue<HttpRequest> _requestQueue = Queue<HttpRequest>();
late HttpServer _io; late HttpServer _io;
AngelHttp? _server; AngelHttp? _server;
Duration? _timeout; late Duration _timeout;
late vm.VmService _client;
vm.VM? _vmachine; vm.VM? _vmachine;
vm.IsolateRef? _mainIsolate;
/// If `true` (default), then developers can `press 'r' to reload` the application on-the-fly. /// If `true` (default), then developers can `press 'r' to reload` the application on-the-fly.
/// ///
@ -47,7 +48,7 @@ class HotReloader {
/// ///
/// If the timeout expires, then the request will be immediately terminated with a `502 Bad Gateway` error. /// If the timeout expires, then the request will be immediately terminated with a `502 Bad Gateway` error.
/// Default: `5s` /// Default: `5s`
Duration? get timeout => _timeout; Duration get timeout => _timeout;
/// The Dart VM service host. /// The Dart VM service host.
/// ///
@ -116,20 +117,22 @@ class HotReloader {
Future handleRequest(HttpRequest request) async { Future handleRequest(HttpRequest request) async {
if (_server != null) { if (_server != null) {
return await _handle(request); return await _handle(request);
} else if (timeout == null) { //} else if (timeout == null) {
_requestQueue.add(request); // _requestQueue.add(request);
} else { } else {
_requestQueue.add(request); _requestQueue.add(request);
Timer(timeout!, () { Timer(timeout, () {
if (_requestQueue.remove(request)) { if (_requestQueue.remove(request)) {
// Send 502 response // Send 502 response
sendError(request, HttpStatus.badGateway, '502 Bad Gateway', 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 { Future<AngelHttp> _generateServer() async {
var s = await generator(); var s = await generator();
await Future.forEach(s.startupHooks, s.configure); await Future.forEach(s.startupHooks, s.configure);
@ -138,23 +141,24 @@ class HotReloader {
} }
void _logWarning(String msg) { void _logWarning(String msg) {
if (_server?.app.logger != null) { if (_appLogger != null) {
_server?.app.logger.warning(msg); _appLogger?.warning(msg);
} else { } else {
print(yellow.wrap('WARNING: $msg')); print(yellow.wrap('WARNING: $msg'));
} }
} }
void _logInfo(String msg) { void _logInfo(String msg) {
if (_server?.app.logger != null) { if (_appLogger != null) {
_server?.app.logger.info(msg); _appLogger?.info(msg);
} else { } else {
print(lightGray.wrap(msg)); print(lightGray.wrap(msg));
} }
} }
/// Starts listening to requests and filesystem events. /// 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; var isHot = true;
_server = await _generateServer(); _server = await _generateServer();
@ -185,9 +189,10 @@ class HotReloader {
_mainIsolate ??= _vmachine?.isolates?.first; _mainIsolate ??= _vmachine?.isolates?.first;
if (_vmachine != null) { if (_vmachine != null) {
for (var isolate in _vmachine!.isolates ?? <vm.IsolateRef>[]) { for (var isolate in _vmachine?.isolates ?? <vm.IsolateRef>[]) {
if (isolate.id != null) { var isolateId = isolate.id;
await _client.setIsolatePauseMode(isolate.id!, if (isolateId != null) {
await _client.setIsolatePauseMode(isolateId,
exceptionPauseMode: 'None'); exceptionPauseMode: 'None');
} }
} }
@ -203,7 +208,8 @@ class HotReloader {
while (_requestQueue.isNotEmpty) { while (_requestQueue.isNotEmpty) {
await _handle(_requestQueue.removeFirst()); 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); server.listen(handleRequest);
// Print a Flutter-like prompt... // Print a Flutter-like prompt...
@ -213,7 +219,7 @@ class HotReloader {
Uri? observatoryUri; Uri? observatoryUri;
if (isHot) { if (isHot) {
observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri!); observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri);
} }
print(styleBold.wrap( print(styleBold.wrap(
@ -260,7 +266,7 @@ class HotReloader {
if (ch == $R) { if (ch == $R) {
_logInfo('Manually restarting server...\n'); _logInfo('Manually restarting server...\n');
await _killServer(); await _killServer();
await _server!.close(); await _server?.close();
var addr = _io.address.address; var addr = _io.address.address;
var port = _io.port; var port = _io.port;
await _io.close(force: true); await _io.close(force: true);
@ -359,21 +365,23 @@ class HotReloader {
scheduleMicrotask(() async { scheduleMicrotask(() async {
// Disconnect active WebSockets // Disconnect active WebSockets
try { try {
var ws = _server!.app.container.make<AngelWebSocket>(); var ws = _server?.app.container.make<AngelWebSocket>();
if (ws != null) {
for (var client in ws.clients) { for (var client in ws.clients) {
try { try {
await client.close(); await client.close();
} catch (e) { } catch (e) {
_logWarning( _logWarning(
'Couldn\'t close WebSocket from session #${client.request.session!.id}: $e'); 'Couldn\'t close WebSocket from session #${client.request.session?.id}: $e');
}
} }
} }
// await Future.forEach( // await Future.forEach(
// _server.app.shutdownHooks, _server.app.configure); // _server.app.shutdownHooks, _server.app.configure);
await _server!.app.close(); await _server?.app.close();
_server!.app.logger.clearListeners(); _server?.app.logger.clearListeners();
} catch (_) { } catch (_) {
// Fail silently... // Fail silently...
} }
@ -387,12 +395,17 @@ class HotReloader {
_server = null; _server = null;
if (hot) { 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) { if (report.success != null) {
_logWarning( _logWarning(
'Hot reload failed - perhaps some sources have not been generated yet.'); 'Hot reload failed - perhaps some sources have not been generated yet.');
} }
} else {
_logWarning('Hot reload failed - isolate id does not exist.');
}
} }
var s = await _generateServer(); var s = await _generateServer();

View file

@ -1,5 +1,5 @@
name: angel3_hot name: angel3_hot
version: 8.0.0 version: 8.1.0
description: Supports hot reloading/hot code push of Angel3 servers on file changes. description: Supports hot reloading/hot code push of Angel3 servers on file changes.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/hot repository: https://github.com/dukefirehawk/angel/tree/master/packages/hot
@ -13,17 +13,17 @@ dependencies:
glob: ^2.1.0 glob: ^2.1.0
io: ^1.0.0 io: ^1.0.0
path: ^1.8.0 path: ^1.8.0
vm_service: ^11.6.0 vm_service: ^13.0.0
watcher: ^1.0.0 watcher: ^1.0.0
logging: ^1.2.0
dev_dependencies: dev_dependencies:
http: ^1.0.0 http: ^1.0.0
logging: ^1.2.0
lints: ^2.1.0 lints: ^2.1.0
# dependency_overrides: dependency_overrides:
# angel3_container: # angel3_container:
# path: ../container/angel_container # path: ../container/angel_container
# angel3_framework: angel3_framework:
# path: ../framework path: ../framework
# angel3_http_exception: # angel3_http_exception:
# path: ../http_exception # path: ../http_exception
# angel3_model: # angel3_model:

View file

@ -1,5 +1,9 @@
# Change Log # Change Log
## 8.1.0
* Added optional `responseHeaders` and `removeResponseHeaders` to `Runner`
## 8.0.0 ## 8.0.0
* Require Dart >= 3.0 * Require Dart >= 3.0

View file

@ -9,9 +9,7 @@ Helpers for concurrency, message-passing, rotating loggers, and other production
![Screenshot](angel3-screenshot.png) ![Screenshot](angel3-screenshot.png)
This will become the de-facto way to run Angel3 applications in deployed environments, as it takes care of inter-isolate communication, respawning dead processes, and other housekeeping for you automatically. This will become the de-facto way to run Angel3 applications in deployed environments, as it takes care of inter-isolate communication, respawning dead processes, and other housekeeping for you automatically. Most users will want to use the `Runner` class.
Most users will want to use the `Runner` class.
## `Runner` ## `Runner`
@ -45,15 +43,11 @@ When combined with `systemd`, deploying Angel3 applications on Linux can be very
## Message Passing ## Message Passing
The `Runner` class uses [`belatuk_pub_sub`](<https://pub.dev/packages/belatuk_pub_sub>) to coordinate The `Runner` class uses [`belatuk_pub_sub`](<https://pub.dev/packages/belatuk_pub_sub>) to coordinate
message passing between isolates. message passing between isolates. When one isolate sends a message, all other isolates will receive the same message, except for the isolate that sent it. It is injected into your application's `Container` as `pub_sub.Client`, so you can use it as follows:
When one isolate sends a message, all other isolates will receive the same message, except for the isolate that sent it.
It is injected into your application's `Container` as `pub_sub.Client`, so you can use it as follows:
```dart ```dart
// Use the injected `pub_sub.Client` to send messages. // Use the injected `pub_sub.Client` to send messages.
var client = app.container.make<pub_sub.Client>(); var client = app.container.make<Client>();
// We can listen for an event to perform some behavior. // We can listen for an event to perform some behavior.
// //
@ -66,6 +60,32 @@ onGreetingChanged
}); });
``` ```
## Customising Response Header
Additional parameters can be passed to the `Runner` class to:
1. Remove headers from HTTP response.
2. Add headers to HTTP response.
For example, the following code snippet removes `X-FRAME-OPTIONS` and adds `X-XSRF_TOKEN` to the response header.
```dart
void main(List<String> args) {
// Remove default
var removeHeader = {'X-FRAME-OPTIONS': 'SAMEORIGIN'};
var customHeader = {
'X-XSRF_TOKEN':
'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'
};
Runner('example', configureServer,
removeResponseHeaders: removeHeader, responseHeaders: customHeader)
.run(args);
}
```
## Run-time Metadata ## Run-time Metadata
At run-time, you may want to know information about the currently-running instance, for example, which number instance. For this, the `InstanceInfo` class is injected into each instance: At run-time, you may want to know information about the currently-running instance, for example, which number instance. For this, the `InstanceInfo` class is injected into each instance:

View file

@ -2,13 +2,16 @@ import 'dart:async';
import 'dart:isolate'; import 'dart:isolate';
import 'package:angel3_framework/angel3_framework.dart'; import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_production/angel3_production.dart'; import 'package:angel3_production/angel3_production.dart';
import 'package:belatuk_pub_sub/belatuk_pub_sub.dart' as pub_sub; import 'package:belatuk_pub_sub/belatuk_pub_sub.dart';
void main(List<String> args) => Runner('example', configureServer).run(args); void main(List<String> args) {
Runner('example', configureServer).run(args);
}
Future configureServer(Angel app) async { Future configureServer(Angel app) async {
// Use the injected `pub_sub.Client` to send messages. // Use the injected `pub_sub.Client` to send messages.
var client = app.container.make<pub_sub.Client>(); var client = app.container.make<Client>();
var greeting = 'Hello! This is the default greeting.'; var greeting = 'Hello! This is the default greeting.';
// We can listen for an event to perform some behavior. // We can listen for an event to perform some behavior.

View file

@ -32,13 +32,12 @@ class RunnerOptions {
..addOption('key-file', help: 'The PEM key file to read.') ..addOption('key-file', help: 'The PEM key file to read.')
..addOption('key-password', help: 'The PEM key file password.'); ..addOption('key-password', help: 'The PEM key file password.');
final String? hostname, final String hostname;
certificateFile, final String? certificateFile, keyFile, certificatePassword, keyPassword;
keyFile,
certificatePassword,
keyPassword;
final int concurrency, port; final int concurrency, port;
final bool useZone, respawn, quiet, ssl, http2; final bool useZone, respawn, quiet, ssl, http2;
final Map<String, Object> removeResponseHeaders = {};
final Map<String, Object> responseHeaders = {};
RunnerOptions( RunnerOptions(
{this.hostname = '127.0.0.1', {this.hostname = '127.0.0.1',
@ -56,26 +55,18 @@ class RunnerOptions {
factory RunnerOptions.fromArgResults(ArgResults argResults) { factory RunnerOptions.fromArgResults(ArgResults argResults) {
return RunnerOptions( return RunnerOptions(
hostname: argResults['address'] as String?, hostname: argResults['address'] as String,
port: int.parse(argResults['port'] as String), port: int.parse(argResults['port'] as String),
concurrency: int.parse(argResults['concurrency'] as String), concurrency: int.parse(argResults['concurrency'] as String),
useZone: argResults['use-zone'] as bool? ?? false, useZone: argResults['use-zone'] as bool? ?? false,
respawn: argResults['respawn'] as bool? ?? true, respawn: argResults['respawn'] as bool? ?? true,
quiet: argResults['quiet'] as bool? ?? false, quiet: argResults['quiet'] as bool? ?? false,
certificateFile: argResults.wasParsed('certificate-file') certificateFile: argResults['certificate-file'] as String?,
? argResults['certificate-file'] as String? keyFile: argResults['key-file'] as String?,
: null,
keyFile: argResults.wasParsed('key-file')
? argResults['key-file'] as String?
: null,
ssl: argResults['ssl'] as bool? ?? false, ssl: argResults['ssl'] as bool? ?? false,
http2: argResults['http2'] as bool? ?? false, http2: argResults['http2'] as bool? ?? false,
certificatePassword: argResults.wasParsed('certificate-password') certificatePassword: argResults['certificate-password'] as String?,
? argResults['certificate-password'] as String? keyPassword: argResults['key-password'] as String?,
: null,
keyPassword: argResults.wasParsed('key-password')
? argResults['key-password'] as String?
: null,
); );
} }
} }

View file

@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';
import 'package:belatuk_pub_sub/belatuk_pub_sub.dart';
import 'package:belatuk_pub_sub/isolate.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:angel3_container/angel3_container.dart'; import 'package:angel3_container/angel3_container.dart';
import 'package:angel3_framework/angel3_framework.dart'; import 'package:angel3_framework/angel3_framework.dart';
@ -10,8 +12,6 @@ import 'package:args/args.dart';
import 'package:io/ansi.dart'; import 'package:io/ansi.dart';
import 'package:io/io.dart'; import 'package:io/io.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:belatuk_pub_sub/isolate.dart' as pub_sub;
import 'package:belatuk_pub_sub/belatuk_pub_sub.dart' as pub_sub;
import 'instance_info.dart'; import 'instance_info.dart';
import 'options.dart'; import 'options.dart';
@ -24,35 +24,21 @@ class Runner {
final AngelConfigurer configureServer; final AngelConfigurer configureServer;
final Reflector reflector; final Reflector reflector;
final Map<String, Object> removeResponseHeaders;
final Map<String, Object> responseHeaders;
Runner(this.name, this.configureServer, Runner(this.name, this.configureServer,
{this.reflector = const EmptyReflector()}); {this.reflector = const EmptyReflector(),
this.removeResponseHeaders = const {},
this.responseHeaders = const {}});
static const String asciiArt2 = ''' static const String _asciiArt = '''
___ _ ________________ _____
/ | / | / / ____/ ____/ / |__ /
/ /| | / |/ / / __/ __/ / / /_ <
/ ___ |/ /| / /_/ / /___/ /______/ /
/_/ |_/_/ |_/\\____/_____/_____/____/
''';
static const String asciiArt = '''
_ _ _ ____ _____ _ _____ _ _ _ ____ _____ _ _____
/ \\ | \\ | |/ ___| ____| | |___ / / \\ | \\ | |/ ___| ____| | |___ /
/ _ \\ | \\| | | _| _| | | |_ \\ / _ \\ | \\| | | _| _| | | |_ \\
/ ___ \\| |\\ | |_| | |___| |___ ___) | / ___ \\| |\\ | |_| | |___| |___ ___) |
/_/ \\_\\_| \\_|\\____|_____|_____|____/ /_/ \\_\\_| \\_|\\____|_____|_____|____/
''';
static const String asciiArtOld = '''
____________ ________________________
___ |__ | / /_ ____/__ ____/__ /
__ /| |_ |/ /_ / __ __ __/ __ /
_ ___ | /| / / /_/ / _ /___ _ /___
/_/ |_/_/ |_/ ____/ /_____/ /_____/
'''; ''';
static final DateFormat _defaultDateFormat = static final DateFormat _defaultDateFormat =
@ -173,11 +159,14 @@ _ ___ | /| / / /_/ / _ /___ _ /___
/// Starts a number of isolates, running identical instances of an Angel application. /// Starts a number of isolates, running identical instances of an Angel application.
Future run(List<String> args) async { Future run(List<String> args) async {
pub_sub.Server? server; Server? server;
try { try {
var argResults = RunnerOptions.argParser.parse(args); var argResults = RunnerOptions.argParser.parse(args);
var options = RunnerOptions.fromArgResults(argResults); var options = RunnerOptions.fromArgResults(argResults);
options.responseHeaders.addAll(responseHeaders);
options.removeResponseHeaders.addAll(removeResponseHeaders);
if (options.ssl || options.http2) { if (options.ssl || options.http2) {
if (options.certificateFile == null) { if (options.certificateFile == null) {
@ -188,7 +177,7 @@ _ ___ | /| / / /_/ / _ /___ _ /___
} }
print(darkGray.wrap( print(darkGray.wrap(
'$asciiArt\n\nA batteries-included, full-featured, full-stack framework in Dart.\n\nhttps://angel3-framework.web.app\n')); '$_asciiArt\n\nA batteries-included, full-featured, full-stack framework in Dart.\n\nhttps://angel3-framework.web.app\n'));
if (argResults['help'] == true) { if (argResults['help'] == true) {
stdout stdout
@ -199,12 +188,12 @@ _ ___ | /| / / /_/ / _ /___ _ /___
print('Starting `$name` application...'); print('Starting `$name` application...');
var adapter = pub_sub.IsolateAdapter(); var adapter = IsolateAdapter();
server = pub_sub.Server([adapter]); server = Server([adapter]);
// Register clients // Register clients
for (var i = 0; i < Platform.numberOfProcessors; i++) { for (var i = 0; i < Platform.numberOfProcessors; i++) {
server.registerClient(pub_sub.ClientInfo('client$i')); server.registerClient(ClientInfo('client$i'));
} }
server.start(); server.start();
@ -240,11 +229,10 @@ _ ___ | /| / / /_/ / _ /___ _ /___
)); ));
zone.run(() async { zone.run(() async {
var client = var client = IsolateClient('client${argsWithId.id}', args.pubSubSendPort);
pub_sub.IsolateClient('client${argsWithId.id}', args.pubSubSendPort);
var app = Angel(reflector: args.reflector) var app = Angel(reflector: args.reflector)
..container.registerSingleton<pub_sub.Client>(client) ..container.registerSingleton<Client>(client)
..container.registerSingleton(InstanceInfo(id: argsWithId.id)); ..container.registerSingleton(InstanceInfo(id: argsWithId.id));
app.shutdownHooks.add((_) => client.close()); app.shutdownHooks.add((_) => client.close());
@ -292,6 +280,13 @@ _ ___ | /| / / /_/ / _ /___ _ /___
} }
await driver.startServer(args.options.hostname, args.options.port); await driver.startServer(args.options.hostname, args.options.port);
// Only apply the headers to AngelHttp instance
if (driver is AngelHttp) {
driver.responseHeader(args.options.responseHeaders);
driver.removeResponseHeader(args.options.removeResponseHeaders);
}
serverUrl = driver.uri; serverUrl = driver.uri;
if (args.options.ssl || args.options.http2) { if (args.options.ssl || args.options.http2) {
serverUrl = serverUrl.replace(scheme: 'https'); serverUrl = serverUrl.replace(scheme: 'https');

View file

@ -1,5 +1,5 @@
name: angel3_production name: angel3_production
version: 8.0.0 version: 8.1.0
description: Helpers for concurrency, message-passing, rotating loggers, and other production functionality in Angel3. description: Helpers for concurrency, message-passing, rotating loggers, and other production functionality in Angel3.
homepage: https://angel3-framework.web.app homepage: https://angel3-framework.web.app
repository: https://github.com/dukefirehawk/angel/tree/master/packages/production repository: https://github.com/dukefirehawk/angel/tree/master/packages/production
@ -7,7 +7,7 @@ environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
angel3_container: ^8.0.0 angel3_container: ^8.0.0
angel3_framework: ^8.0.0 angel3_framework: ^8.2.0
belatuk_pub_sub: ^6.0.0 belatuk_pub_sub: ^6.0.0
args: ^2.4.0 args: ^2.4.0
io: ^1.0.0 io: ^1.0.0
@ -15,11 +15,11 @@ dependencies:
intl: ^0.18.0 intl: ^0.18.0
dev_dependencies: dev_dependencies:
lints: ^2.1.0 lints: ^2.1.0
# dependency_overrides: dependency_overrides:
# angel3_container: # angel3_container:
# path: ../container/angel_container # path: ../container/angel_container
# angel3_framework: angel3_framework:
# path: ../framework path: ../framework
# angel3_http_exception: # angel3_http_exception:
# path: ../http_exception # path: ../http_exception
# angel3_model: # angel3_model: