Updated angel framework to NNBD
This commit is contained in:
parent
0fe0a5a56b
commit
fdf1532074
58 changed files with 860 additions and 812 deletions
|
@ -8,9 +8,11 @@
|
|||
* Updated angel_route to 5.0.0
|
||||
* Updated angel_model to 3.0.0
|
||||
* Updated angel_container to 3.0.0
|
||||
* Updated angel_framework to 4.0.0
|
||||
- merge_map
|
||||
- mock_request
|
||||
* Added merge_map and updated to 2.0.0
|
||||
* Added mock_request and updated to 2.0.0
|
||||
* Updated angel_framework to 4.0.0 (Revisit TODO)
|
||||
* Updated angel_auth to 4.0.0 (todo)
|
||||
* Updated angel_configuration to 4.0.0 (todo)
|
||||
|
||||
# 3.0.0 (Non NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
|
||||
|
|
|
@ -10,17 +10,17 @@ dependencies:
|
|||
angel_framework:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/framework
|
||||
charcode: ^1.0.0
|
||||
collection: ^1.0.0
|
||||
charcode: ^1.2.0
|
||||
collection: ^1.15.0
|
||||
crypto: ^3.0.0
|
||||
http_parser: ^4.0.0
|
||||
meta: ^1.0.0
|
||||
quiver_hashcode: ^2.0.0
|
||||
meta: ^1.3.0
|
||||
quiver_hashcode: ^3.0.0+1
|
||||
dev_dependencies:
|
||||
http: ^0.13.0
|
||||
http: ^0.13.1
|
||||
io: ^1.0.0
|
||||
logging: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
test: ^1.15.7
|
||||
pedantic: ^1.11.0
|
||||
test: ^1.16.8
|
||||
|
|
|
@ -16,7 +16,7 @@ main() async {
|
|||
// Simple fallback to throw a 404 on unknown paths.
|
||||
app.fallback((req, res) {
|
||||
throw AngelHttpException.notFound(
|
||||
message: 'Unknown path: "${req.uri.path}"',
|
||||
message: 'Unknown path: "${req.uri!.path}"',
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -40,7 +40,7 @@ class ArtistsController extends Controller {
|
|||
form(RequestContext req) async {
|
||||
// Deserialize the body into an artist.
|
||||
var artist = await req.deserializeBody((m) {
|
||||
return Artist(name: m['name'] as String ?? '(unknown name)');
|
||||
return Artist(name: m!['name'] as String? ?? '(unknown name)');
|
||||
});
|
||||
|
||||
// Return it (it will be serialized to JSON).
|
||||
|
@ -49,7 +49,7 @@ class ArtistsController extends Controller {
|
|||
}
|
||||
|
||||
class Artist {
|
||||
final String name;
|
||||
final String? name;
|
||||
|
||||
Artist({this.name});
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ main() async {
|
|||
(req, res) => Future.error('Throwing just because I feel like!'));
|
||||
|
||||
var http = AngelHttp(app);
|
||||
var server = await http.startServer('127.0.0.1', 3000);
|
||||
HttpServer server = await http.startServer('127.0.0.1', 3000);
|
||||
var url = 'http://${server.address.address}:${server.port}';
|
||||
print('Listening at $url');
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ main() async {
|
|||
try {
|
||||
ctx.setAlpnProtocols(['h2'], true);
|
||||
} catch (e, st) {
|
||||
app.logger.severe(
|
||||
app.logger!.severe(
|
||||
'Cannot set ALPN protocol on server to `h2`. The server will only serve HTTP/1.x.',
|
||||
e,
|
||||
st);
|
||||
|
@ -41,6 +41,6 @@ main() async {
|
|||
// HTTP/1.x requests will fallback to `AngelHttp`
|
||||
http2.onHttp1.listen(http1.handleRequest);
|
||||
|
||||
var server = await http2.startServer('127.0.0.1', 3000);
|
||||
SecureServerSocket server = await http2.startServer('127.0.0.1', 3000);
|
||||
print('Listening at https://${server.address.address}:${server.port}');
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ main() async {
|
|||
try {
|
||||
ctx.setAlpnProtocols(['h2'], true);
|
||||
} catch (e, st) {
|
||||
app.logger.severe(
|
||||
app.logger!.severe(
|
||||
'Cannot set ALPN protocol on server to `h2`. The server will only serve HTTP/1.x.',
|
||||
e,
|
||||
st,
|
||||
|
|
|
@ -45,7 +45,7 @@ main() async {
|
|||
try {
|
||||
ctx.setAlpnProtocols(['h2'], true);
|
||||
} catch (e, st) {
|
||||
app.logger.severe(
|
||||
app.logger!.severe(
|
||||
'Cannot set ALPN protocol on server to `h2`. The server will only serve HTTP/1.x.',
|
||||
e,
|
||||
st);
|
||||
|
@ -57,6 +57,6 @@ main() async {
|
|||
// HTTP/1.x requests will fallback to `AngelHttp`
|
||||
http2.onHttp1.listen(http1.handleRequest);
|
||||
|
||||
var server = await http2.startServer('127.0.0.1', 3000);
|
||||
SecureServerSocket server = await http2.startServer('127.0.0.1', 3000);
|
||||
print('Listening at https://${server.address.address}:${server.port}');
|
||||
}
|
||||
|
|
|
@ -48,6 +48,6 @@ serverMain(_) async {
|
|||
print(e.stackTrace);
|
||||
};
|
||||
|
||||
var server = await http.startServer('127.0.0.1', 3000);
|
||||
HttpServer server = await http.startServer('127.0.0.1', 3000);
|
||||
print('Listening at http://${server.address.address}:${server.port}');
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:angel_container/mirrors.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
|
@ -42,12 +44,12 @@ main() async {
|
|||
// Simple fallback to throw a 404 on unknown paths.
|
||||
app.fallback((req, res) {
|
||||
throw AngelHttpException.notFound(
|
||||
message: 'Unknown path: "${req.uri.path}"',
|
||||
message: 'Unknown path: "${req.uri!.path}"',
|
||||
);
|
||||
});
|
||||
|
||||
var http = AngelHttp(app);
|
||||
var server = await http.startServer('127.0.0.1', 3000);
|
||||
HttpServer server = await http.startServer('127.0.0.1', 3000);
|
||||
var url = 'http://${server.address.address}:${server.port}';
|
||||
print('Listening at $url');
|
||||
print('Visit these pages to see Angel in action:');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:angel_container/mirrors.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
|
@ -12,7 +14,7 @@ main() async {
|
|||
app.get('/', (req, res) => res.render('index', {'foo': 'bar'}));
|
||||
|
||||
var http = AngelHttp(app);
|
||||
var server = await http.startServer('127.0.0.1', 3000);
|
||||
HttpServer server = await http.startServer('127.0.0.1', 3000);
|
||||
var url = 'http://${server.address.address}:${server.port}';
|
||||
print('Listening at $url');
|
||||
}
|
||||
|
|
|
@ -7,19 +7,19 @@ import 'service.dart';
|
|||
///
|
||||
/// Well-suited for testing.
|
||||
class AnonymousService<Id, Data> extends Service<Id, Data> {
|
||||
FutureOr<List<Data>> Function([Map<String, dynamic>]) _index;
|
||||
FutureOr<Data> Function(Id, [Map<String, dynamic>]) _read, _remove;
|
||||
FutureOr<Data> Function(Data, [Map<String, dynamic>]) _create;
|
||||
FutureOr<Data> Function(Id, Data, [Map<String, dynamic>]) _modify, _update;
|
||||
FutureOr<List<Data>>? Function([Map<String, dynamic>?])? _index;
|
||||
FutureOr<Data> Function(Id?, [Map<String, dynamic>?])? _read, _remove;
|
||||
FutureOr<Data> Function(Data, [Map<String, dynamic>?])? _create;
|
||||
FutureOr<Data> Function(Id?, Data, [Map<String, dynamic>?])? _modify, _update;
|
||||
|
||||
AnonymousService(
|
||||
{FutureOr<List<Data>> index([Map<String, dynamic> params]),
|
||||
FutureOr<Data> read(Id id, [Map<String, dynamic> params]),
|
||||
FutureOr<Data> create(Data data, [Map<String, dynamic> params]),
|
||||
FutureOr<Data> modify(Id id, Data data, [Map<String, dynamic> params]),
|
||||
FutureOr<Data> update(Id id, Data data, [Map<String, dynamic> params]),
|
||||
FutureOr<Data> remove(Id id, [Map<String, dynamic> params]),
|
||||
FutureOr<Data> Function(RequestContext, ResponseContext) readData})
|
||||
{FutureOr<List<Data>>? index([Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> read(Id? id, [Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> create(Data data, [Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> modify(Id? id, Data data, [Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> update(Id? id, Data data, [Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> remove(Id? id, [Map<String, dynamic>? params])?,
|
||||
FutureOr<Data> Function(RequestContext, ResponseContext?)? readData})
|
||||
: super(readData: readData) {
|
||||
_index = index;
|
||||
_read = read;
|
||||
|
@ -30,30 +30,30 @@ class AnonymousService<Id, Data> extends Service<Id, Data> {
|
|||
}
|
||||
|
||||
@override
|
||||
index([Map<String, dynamic> params]) =>
|
||||
Future.sync(() => _index != null ? _index(params) : super.index(params));
|
||||
index([Map<String, dynamic>? params]) =>
|
||||
Future.sync(() => _index != null ? _index!(params)! : super.index(params));
|
||||
|
||||
@override
|
||||
read(Id id, [Map<String, dynamic> params]) => Future.sync(
|
||||
() => _read != null ? _read(id, params) : super.read(id, params));
|
||||
read(Id? id, [Map<String, dynamic>? params]) => Future.sync(
|
||||
() => _read != null ? _read!(id, params) : super.read(id, params));
|
||||
|
||||
@override
|
||||
create(Data data, [Map<String, dynamic> params]) => Future.sync(() =>
|
||||
_create != null ? _create(data, params) : super.create(data, params));
|
||||
create(Data data, [Map<String, dynamic>? params]) => Future.sync(() =>
|
||||
_create != null ? _create!(data, params) : super.create(data, params));
|
||||
|
||||
@override
|
||||
modify(Id id, Data data, [Map<String, dynamic> params]) =>
|
||||
modify(Id? id, Data data, [Map<String, dynamic>? params]) =>
|
||||
Future.sync(() => _modify != null
|
||||
? _modify(id, data, params)
|
||||
? _modify!(id, data, params)
|
||||
: super.modify(id, data, params));
|
||||
|
||||
@override
|
||||
update(Id id, Data data, [Map<String, dynamic> params]) =>
|
||||
update(Id? id, Data data, [Map<String, dynamic>? params]) =>
|
||||
Future.sync(() => _update != null
|
||||
? _update(id, data, params)
|
||||
? _update!(id, data, params)
|
||||
: super.update(id, data, params));
|
||||
|
||||
@override
|
||||
remove(Id id, [Map<String, dynamic> params]) => Future.sync(
|
||||
() => _remove != null ? _remove(id, params) : super.remove(id, params));
|
||||
remove(Id? id, [Map<String, dynamic>? params]) => Future.sync(
|
||||
() => _remove != null ? _remove!(id, params) : super.remove(id, params));
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import '../core/core.dart';
|
|||
|
||||
/// Supports grouping routes with shared functionality.
|
||||
class Controller {
|
||||
Angel _app;
|
||||
Angel? _app;
|
||||
|
||||
/// The [Angel] application powering this controller.
|
||||
Angel get app => _app;
|
||||
Angel? get app => _app;
|
||||
|
||||
/// If `true` (default), this class will inject itself as a singleton into the [app]'s container when bootstrapped.
|
||||
final bool injectSingleton;
|
||||
|
@ -21,12 +21,12 @@ class Controller {
|
|||
List<RequestHandler> middleware = [];
|
||||
|
||||
/// A mapping of route paths to routes, produced from the [Expose] annotations on this class.
|
||||
Map<String, Route> routeMappings = {};
|
||||
Map<String?, Route> routeMappings = {};
|
||||
|
||||
SymlinkRoute<RequestHandler> _mountPoint;
|
||||
SymlinkRoute<RequestHandler?>? _mountPoint;
|
||||
|
||||
/// The route at which this controller is mounted on the server.
|
||||
SymlinkRoute<RequestHandler> get mountPoint => _mountPoint;
|
||||
SymlinkRoute<RequestHandler?>? get mountPoint => _mountPoint;
|
||||
|
||||
Controller({this.injectSingleton = true});
|
||||
|
||||
|
@ -36,35 +36,35 @@ class Controller {
|
|||
_app = app;
|
||||
|
||||
if (injectSingleton != false) {
|
||||
if (!app.container.has(runtimeType)) {
|
||||
_app.container.registerSingleton(this, as: runtimeType);
|
||||
if (!app.container!.has(runtimeType)) {
|
||||
_app!.container!.registerSingleton(this, as: runtimeType);
|
||||
}
|
||||
}
|
||||
|
||||
var name = await applyRoutes(app, app.container.reflector);
|
||||
var name = await applyRoutes(app, app.container!.reflector);
|
||||
app.controllers[name] = this;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Applies the routes from this [Controller] to some [router].
|
||||
Future<String> applyRoutes(
|
||||
Router<RequestHandler> router, Reflector reflector) async {
|
||||
Future<String?> applyRoutes(
|
||||
Router<RequestHandler?> router, Reflector reflector) async {
|
||||
// Load global expose decl
|
||||
var classMirror = reflector.reflectClass(this.runtimeType);
|
||||
Expose exposeDecl = findExpose(reflector);
|
||||
var classMirror = reflector.reflectClass(this.runtimeType)!;
|
||||
Expose? exposeDecl = findExpose(reflector);
|
||||
|
||||
if (exposeDecl == null) {
|
||||
throw Exception("All controllers must carry an @Expose() declaration.");
|
||||
}
|
||||
|
||||
var routable = Routable();
|
||||
_mountPoint = router.mount(exposeDecl.path, routable);
|
||||
_mountPoint = router.mount(exposeDecl.path!, routable);
|
||||
var typeMirror = reflector.reflectType(this.runtimeType);
|
||||
|
||||
// Pre-reflect methods
|
||||
var instanceMirror = reflector.reflectInstance(this);
|
||||
final handlers = <RequestHandler>[]
|
||||
..addAll(exposeDecl.middleware)
|
||||
..addAll(exposeDecl.middleware!)
|
||||
..addAll(middleware);
|
||||
final routeBuilder =
|
||||
_routeBuilder(reflector, instanceMirror, routable, handlers);
|
||||
|
@ -72,12 +72,12 @@ class Controller {
|
|||
classMirror.declarations.forEach(routeBuilder);
|
||||
|
||||
// Return the name.
|
||||
return exposeDecl.as?.isNotEmpty == true ? exposeDecl.as : typeMirror.name;
|
||||
return exposeDecl.as?.isNotEmpty == true ? exposeDecl.as : typeMirror!.name;
|
||||
}
|
||||
|
||||
void Function(ReflectedDeclaration) _routeBuilder(
|
||||
Reflector reflector,
|
||||
ReflectedInstance instanceMirror,
|
||||
ReflectedInstance? instanceMirror,
|
||||
Routable routable,
|
||||
Iterable<RequestHandler> handlers) {
|
||||
return (ReflectedDeclaration decl) {
|
||||
|
@ -89,13 +89,13 @@ class Controller {
|
|||
methodName != 'call' &&
|
||||
methodName != 'equals' &&
|
||||
methodName != '==') {
|
||||
var exposeDecl = decl.function.annotations
|
||||
var exposeDecl = decl.function!.annotations
|
||||
.map((m) => m.reflectee)
|
||||
.firstWhere((r) => r is Expose, orElse: () => null) as Expose;
|
||||
.firstWhere((r) => r is Expose, orElse: () => null) as Expose?;
|
||||
|
||||
if (exposeDecl == null) {
|
||||
// If this has a @noExpose, return null.
|
||||
if (decl.function.annotations.any((m) => m.reflectee is NoExpose)) {
|
||||
if (decl.function!.annotations.any((m) => m.reflectee is NoExpose)) {
|
||||
return;
|
||||
} else {
|
||||
// Otherwise, create an @Expose.
|
||||
|
@ -104,15 +104,15 @@ class Controller {
|
|||
}
|
||||
|
||||
var reflectedMethod =
|
||||
instanceMirror.getField(methodName).reflectee as Function;
|
||||
instanceMirror!.getField(methodName).reflectee as Function?;
|
||||
var middleware = <RequestHandler>[]
|
||||
..addAll(handlers)
|
||||
..addAll(exposeDecl.middleware);
|
||||
String name =
|
||||
..addAll(exposeDecl.middleware!);
|
||||
String? name =
|
||||
exposeDecl.as?.isNotEmpty == true ? exposeDecl.as : methodName;
|
||||
|
||||
// Check if normal
|
||||
var method = decl.function;
|
||||
var method = decl.function!;
|
||||
if (method.parameters.length == 2 &&
|
||||
method.parameters[0].type.reflectedType == RequestContext &&
|
||||
method.parameters[1].type.reflectedType == ResponseContext) {
|
||||
|
@ -120,13 +120,13 @@ class Controller {
|
|||
routeMappings[name] = routable
|
||||
.addRoute(exposeDecl.method, exposeDecl.path,
|
||||
(RequestContext req, ResponseContext res) {
|
||||
var result = reflectedMethod(req, res);
|
||||
var result = reflectedMethod!(req, res);
|
||||
return result is RequestHandler ? result(req, res) : result;
|
||||
}, middleware: middleware);
|
||||
return;
|
||||
}
|
||||
|
||||
var injection = preInject(reflectedMethod, reflector);
|
||||
var injection = preInject(reflectedMethod!, reflector);
|
||||
|
||||
if (exposeDecl?.allowNull?.isNotEmpty == true) {
|
||||
injection.optional?.addAll(exposeDecl.allowNull);
|
||||
|
@ -148,7 +148,7 @@ class Controller {
|
|||
var restPath = ReCase(rest.isEmpty ? 'index' : rest)
|
||||
.snakeCase
|
||||
.replaceAll(_rgxMultipleUnderscores, '_');
|
||||
httpMethod = methodMatch[1].toUpperCase();
|
||||
httpMethod = methodMatch[1]!.toUpperCase();
|
||||
|
||||
if (['index', 'by_id'].contains(restPath)) {
|
||||
parts.add('/');
|
||||
|
@ -215,12 +215,12 @@ class Controller {
|
|||
///
|
||||
/// If [concreteOnly] is `false`, then if there is no actual
|
||||
/// [Expose], one will be automatically created.
|
||||
Expose findExpose(Reflector reflector, {bool concreteOnly = false}) {
|
||||
Expose? findExpose(Reflector reflector, {bool concreteOnly = false}) {
|
||||
var existing = reflector
|
||||
.reflectClass(runtimeType)
|
||||
.reflectClass(runtimeType)!
|
||||
.annotations
|
||||
.map((m) => m.reflectee)
|
||||
.firstWhere((r) => r is Expose, orElse: () => null) as Expose;
|
||||
.firstWhere((r) => r is Expose, orElse: () => null) as Expose?;
|
||||
return existing ??
|
||||
(concreteOnly
|
||||
? null
|
||||
|
|
|
@ -17,11 +17,11 @@ abstract class Driver<
|
|||
Server extends Stream<Request>,
|
||||
RequestContextType extends RequestContext,
|
||||
ResponseContextType extends ResponseContext> {
|
||||
final Angel app;
|
||||
final Angel? app;
|
||||
final bool useZone;
|
||||
bool _closed = false;
|
||||
Server _server;
|
||||
StreamSubscription<Request> _sub;
|
||||
Server? _server;
|
||||
StreamSubscription<Request>? _sub;
|
||||
|
||||
/// The function used to bind this instance to a server..
|
||||
final Future<Server> Function(dynamic, int) serverGenerator;
|
||||
|
@ -32,25 +32,27 @@ abstract class Driver<
|
|||
Uri get uri;
|
||||
|
||||
/// The native server running this instance.
|
||||
Server get server => _server;
|
||||
Server? get server => _server;
|
||||
|
||||
Future<Server> generateServer(address, int port) =>
|
||||
serverGenerator(address, port);
|
||||
|
||||
/// Starts, and returns the server.
|
||||
Future<Server> startServer([address, int port]) {
|
||||
Future<Server> startServer([address, int? port]) {
|
||||
var host = address ?? '127.0.0.1';
|
||||
return generateServer(host, port ?? 0).then((server) {
|
||||
_server = server;
|
||||
return Future.wait(app.startupHooks.map(app.configure)).then((_) {
|
||||
app.optimizeForProduction();
|
||||
return Future.wait(app!.startupHooks.map(app!.configure)).then((_) {
|
||||
app!.optimizeForProduction();
|
||||
_sub = server.listen((request) {
|
||||
var stream = createResponseStreamFromRawRequest(request);
|
||||
stream.listen((response) {
|
||||
return handleRawRequest(request, response);
|
||||
// TODO: To be revisited
|
||||
handleRawRequest(request, response);
|
||||
return;
|
||||
});
|
||||
});
|
||||
return _server;
|
||||
return _server!;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -60,8 +62,9 @@ abstract class Driver<
|
|||
if (_closed) return Future.value(_server);
|
||||
_closed = true;
|
||||
_sub?.cancel();
|
||||
return app.close().then((_) =>
|
||||
Future.wait(app.shutdownHooks.map(app.configure)).then((_) => _server));
|
||||
return app!.close().then((_) =>
|
||||
Future.wait(app!.shutdownHooks.map(app!.configure))
|
||||
.then((_) => _server!));
|
||||
}
|
||||
|
||||
Future<RequestContextType> createRequestContext(
|
||||
|
@ -69,9 +72,9 @@ abstract class Driver<
|
|||
|
||||
Future<ResponseContextType> createResponseContext(
|
||||
Request request, Response response,
|
||||
[RequestContextType correspondingRequest]);
|
||||
[RequestContextType? correspondingRequest]);
|
||||
|
||||
void setHeader(Response response, String key, String value);
|
||||
void setHeader(Response response, String key, String? value);
|
||||
|
||||
void setContentLength(Response response, int length);
|
||||
|
||||
|
@ -97,42 +100,42 @@ abstract class Driver<
|
|||
var path = req.path;
|
||||
if (path == '/') path = '';
|
||||
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
|
||||
Tuple4<List?, Map<String?, dynamic>, ParseResult<RouteResult?>?,
|
||||
MiddlewarePipeline> resolveTuple() {
|
||||
var r = app.optimizedRouter;
|
||||
var r = app!.optimizedRouter;
|
||||
var resolved =
|
||||
r.resolveAbsolute(path, method: req.method, strip: false);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler>(resolved);
|
||||
r.resolveAbsolute(path, method: req.method!, strip: false);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler?>(resolved);
|
||||
return Tuple4(
|
||||
pipeline.handlers,
|
||||
resolved.fold<Map<String, dynamic>>(
|
||||
<String, dynamic>{}, (out, r) => out..addAll(r.allParams)),
|
||||
resolved.fold<Map<String?, dynamic>>(
|
||||
<String?, dynamic>{}, (out, r) => out..addAll(r.allParams)),
|
||||
resolved.isEmpty ? null : resolved.first.parseResult,
|
||||
pipeline,
|
||||
);
|
||||
}
|
||||
|
||||
var cacheKey = req.method + path;
|
||||
var tuple = app.environment.isProduction
|
||||
? app.handlerCache.putIfAbsent(cacheKey, resolveTuple)
|
||||
var cacheKey = req.method! + path!;
|
||||
var tuple = app!.environment.isProduction
|
||||
? app!.handlerCache.putIfAbsent(cacheKey, resolveTuple)
|
||||
: resolveTuple();
|
||||
var line = tuple.item4 as MiddlewarePipeline<RequestHandler>;
|
||||
var it = MiddlewarePipelineIterator<RequestHandler>(line);
|
||||
var line = tuple.item4 as MiddlewarePipeline<RequestHandler?>;
|
||||
var it = MiddlewarePipelineIterator<RequestHandler?>(line);
|
||||
|
||||
req.params.addAll(tuple.item2);
|
||||
|
||||
req.container
|
||||
..registerSingleton<RequestContext>(req)
|
||||
?..registerSingleton<RequestContext>(req)
|
||||
..registerSingleton<ResponseContext>(res)
|
||||
..registerSingleton<MiddlewarePipeline>(tuple.item4)
|
||||
..registerSingleton<MiddlewarePipeline<RequestHandler>>(line)
|
||||
..registerSingleton<MiddlewarePipeline<RequestHandler?>>(line)
|
||||
..registerSingleton<MiddlewarePipelineIterator>(it)
|
||||
..registerSingleton<MiddlewarePipelineIterator<RequestHandler>>(it)
|
||||
..registerSingleton<ParseResult<RouteResult>>(tuple.item3)
|
||||
..registerSingleton<ParseResult>(tuple.item3);
|
||||
..registerSingleton<MiddlewarePipelineIterator<RequestHandler?>>(it)
|
||||
..registerSingleton<ParseResult<RouteResult?>?>(tuple.item3)
|
||||
..registerSingleton<ParseResult?>(tuple.item3);
|
||||
|
||||
if (!app.environment.isProduction && app.logger != null) {
|
||||
req.container.registerSingleton<Stopwatch>(Stopwatch()..start());
|
||||
if (!app!.environment.isProduction && app!.logger != null) {
|
||||
req.container!.registerSingleton<Stopwatch>(Stopwatch()..start());
|
||||
}
|
||||
|
||||
return runPipeline(it, req, res, app)
|
||||
|
@ -161,10 +164,10 @@ abstract class Driver<
|
|||
(ee, StackTrace st) {
|
||||
var e = ee as AngelHttpException;
|
||||
|
||||
if (app.logger != null) {
|
||||
if (app!.logger != null) {
|
||||
var error = e.error ?? e;
|
||||
var trace = Trace.from(e.stackTrace ?? StackTrace.current).terse;
|
||||
app.logger.severe(e.message ?? e.toString(), error, trace);
|
||||
app!.logger!.severe(e.message ?? e.toString(), error, trace);
|
||||
}
|
||||
|
||||
return handleAngelHttpException(
|
||||
|
@ -173,16 +176,17 @@ abstract class Driver<
|
|||
} else {
|
||||
var zoneSpec = ZoneSpecification(
|
||||
print: (self, parent, zone, line) {
|
||||
if (app.logger != null) {
|
||||
app.logger.info(line);
|
||||
if (app!.logger != null) {
|
||||
app!.logger!.info(line);
|
||||
} else {
|
||||
parent.print(zone, line);
|
||||
}
|
||||
},
|
||||
handleUncaughtError: (self, parent, zone, error, stackTrace) {
|
||||
var trace = Trace.from(stackTrace ?? StackTrace.current).terse;
|
||||
var trace = Trace.from(stackTrace).terse;
|
||||
|
||||
return Future(() {
|
||||
// TODO: To be revisited
|
||||
Future(() {
|
||||
AngelHttpException e;
|
||||
|
||||
if (error is FormatException) {
|
||||
|
@ -191,24 +195,22 @@ abstract class Driver<
|
|||
e = error;
|
||||
} else {
|
||||
e = AngelHttpException(error,
|
||||
stackTrace: stackTrace,
|
||||
message:
|
||||
error?.toString() ?? '500 Internal Server Error');
|
||||
stackTrace: stackTrace, message: error.toString());
|
||||
}
|
||||
|
||||
if (app.logger != null) {
|
||||
app.logger.severe(e.message ?? e.toString(), error, trace);
|
||||
if (app!.logger != null) {
|
||||
app!.logger!.severe(e.message ?? e.toString(), error, trace);
|
||||
}
|
||||
|
||||
return handleAngelHttpException(
|
||||
e, trace, req, res, request, response);
|
||||
}).catchError((e, StackTrace st) {
|
||||
var trace = Trace.from(st ?? StackTrace.current).terse;
|
||||
var trace = Trace.from(st).terse;
|
||||
closeResponse(response);
|
||||
// Ideally, we won't be in a position where an absolutely fatal error occurs,
|
||||
// but if so, we'll need to log it.
|
||||
if (app.logger != null) {
|
||||
app.logger.severe(
|
||||
if (app!.logger != null) {
|
||||
app!.logger!.severe(
|
||||
'Fatal error occurred when processing $uri.', e, trace);
|
||||
} else {
|
||||
stderr
|
||||
|
@ -218,12 +220,13 @@ abstract class Driver<
|
|||
..writeln(trace);
|
||||
}
|
||||
});
|
||||
return;
|
||||
},
|
||||
);
|
||||
|
||||
var zone = Zone.current.fork(specification: zoneSpec);
|
||||
req.container.registerSingleton<Zone>(zone);
|
||||
req.container.registerSingleton<ZoneSpecification>(zoneSpec);
|
||||
req.container!.registerSingleton<Zone>(zone);
|
||||
req.container!.registerSingleton<ZoneSpecification>(zoneSpec);
|
||||
|
||||
// If a synchronous error is thrown, it's not caught by `zone.run`,
|
||||
// so use a try/catch, and recover when need be.
|
||||
|
@ -240,17 +243,17 @@ abstract class Driver<
|
|||
}
|
||||
|
||||
/// Handles an [AngelHttpException].
|
||||
Future handleAngelHttpException(
|
||||
Future? handleAngelHttpException(
|
||||
AngelHttpException e,
|
||||
StackTrace st,
|
||||
RequestContext req,
|
||||
ResponseContext res,
|
||||
RequestContext? req,
|
||||
ResponseContext? res,
|
||||
Request request,
|
||||
Response response,
|
||||
{bool ignoreFinalizers = false}) {
|
||||
if (req == null || res == null) {
|
||||
try {
|
||||
app.logger?.severe(null, e, st);
|
||||
app!.logger?.severe(null, e, st);
|
||||
setStatusCode(response, 500);
|
||||
writeStringToResponse(response, '500 Internal Server Error');
|
||||
closeResponse(response);
|
||||
|
@ -266,8 +269,8 @@ abstract class Driver<
|
|||
} else {
|
||||
res.statusCode = e.statusCode;
|
||||
handleError =
|
||||
Future.sync(() => app.errorHandler(e, req, res)).then((result) {
|
||||
return app.executeHandler(result, req, res).then((_) => res.close());
|
||||
Future.sync(() => app!.errorHandler(e, req, res)).then((result) {
|
||||
return app!.executeHandler(result, req, res).then((_) => res.close());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -280,11 +283,11 @@ abstract class Driver<
|
|||
ResponseContext res,
|
||||
{bool ignoreFinalizers = false}) {
|
||||
Future<void> _cleanup(_) {
|
||||
if (!app.environment.isProduction &&
|
||||
app.logger != null &&
|
||||
req.container.has<Stopwatch>()) {
|
||||
var sw = req.container.make<Stopwatch>();
|
||||
app.logger.info(
|
||||
if (!app!.environment.isProduction &&
|
||||
app!.logger != null &&
|
||||
req.container!.has<Stopwatch>()) {
|
||||
var sw = req.container!.make<Stopwatch>();
|
||||
app!.logger!.info(
|
||||
"${res.statusCode} ${req.method} ${req.uri} (${sw?.elapsedMilliseconds ?? 'unknown'} ms)");
|
||||
}
|
||||
return req.close();
|
||||
|
@ -294,7 +297,7 @@ abstract class Driver<
|
|||
|
||||
Future finalizers = ignoreFinalizers == true
|
||||
? Future.value()
|
||||
: Future.forEach(app.responseFinalizers, (f) => f(req, res));
|
||||
: Future.forEach(app!.responseFinalizers, (dynamic f) => f(req, res));
|
||||
|
||||
return finalizers.then((_) {
|
||||
//if (res.isOpen) res.close();
|
||||
|
@ -303,18 +306,18 @@ abstract class Driver<
|
|||
setHeader(response, key, res.headers[key]);
|
||||
}
|
||||
|
||||
setContentLength(response, res.buffer.length);
|
||||
setContentLength(response, res.buffer!.length);
|
||||
setChunkedEncoding(response, res.chunked ?? true);
|
||||
|
||||
List<int> outputBuffer = res.buffer.toBytes();
|
||||
List<int> outputBuffer = res.buffer!.toBytes();
|
||||
|
||||
if (res.encoders.isNotEmpty) {
|
||||
var allowedEncodings = req.headers
|
||||
var allowedEncodings = req.headers!
|
||||
.value('accept-encoding')
|
||||
?.split(',')
|
||||
?.map((s) => s.trim())
|
||||
?.where((s) => s.isNotEmpty)
|
||||
?.map((str) {
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((str) {
|
||||
// Ignore quality specifications in accept-encoding
|
||||
// ex. gzip;q=0.8
|
||||
if (!str.contains(';')) return str;
|
||||
|
@ -323,7 +326,7 @@ abstract class Driver<
|
|||
|
||||
if (allowedEncodings != null) {
|
||||
for (var encodingName in allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (res.encoders.containsKey(encodingName)) {
|
||||
|
@ -334,7 +337,7 @@ abstract class Driver<
|
|||
|
||||
if (encoder != null) {
|
||||
setHeader(response, 'content-encoding', key);
|
||||
outputBuffer = res.encoders[key].convert(outputBuffer);
|
||||
outputBuffer = res.encoders[key]!.convert(outputBuffer);
|
||||
setContentLength(response, outputBuffer.length);
|
||||
break;
|
||||
}
|
||||
|
@ -352,16 +355,16 @@ abstract class Driver<
|
|||
/// Runs a [MiddlewarePipeline].
|
||||
static Future<void> runPipeline<RequestContextType extends RequestContext,
|
||||
ResponseContextType extends ResponseContext>(
|
||||
MiddlewarePipelineIterator<RequestHandler> it,
|
||||
MiddlewarePipelineIterator<RequestHandler?> it,
|
||||
RequestContextType req,
|
||||
ResponseContextType res,
|
||||
Angel app) async {
|
||||
Angel? app) async {
|
||||
var broken = false;
|
||||
while (it.moveNext()) {
|
||||
var current = it.current.handlers.iterator;
|
||||
|
||||
while (!broken && current.moveNext()) {
|
||||
var result = await app.executeHandler(current.current, req, res);
|
||||
var result = await app!.executeHandler(current.current, req, res);
|
||||
if (result != true) {
|
||||
broken = true;
|
||||
break;
|
||||
|
|
|
@ -5,7 +5,7 @@ const AngelEnvironment angelEnv = AngelEnvironment();
|
|||
|
||||
/// Queries the environment's `ANGEL_ENV` value.
|
||||
class AngelEnvironment {
|
||||
final String _customValue;
|
||||
final String? _customValue;
|
||||
|
||||
/// You can optionally provide a custom value, in order to override the system's
|
||||
/// value.
|
||||
|
|
|
@ -49,20 +49,20 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
FutureOr<Data> Function(RequestContext, ResponseContext) get readData =>
|
||||
FutureOr<Data>? Function(RequestContext, ResponseContext?)? get readData =>
|
||||
inner.readData;
|
||||
|
||||
RequestContext _getRequest(Map params) {
|
||||
RequestContext? _getRequest(Map? params) {
|
||||
if (params == null) return null;
|
||||
return params['__requestctx'] as RequestContext;
|
||||
return params['__requestctx'] as RequestContext?;
|
||||
}
|
||||
|
||||
ResponseContext _getResponse(Map params) {
|
||||
ResponseContext? _getResponse(Map? params) {
|
||||
if (params == null) return null;
|
||||
return params['__responsectx'] as ResponseContext;
|
||||
return params['__responsectx'] as ResponseContext?;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _stripReq(Map<String, dynamic> params) {
|
||||
Map<String, dynamic>? _stripReq(Map<String, dynamic>? params) {
|
||||
if (params == null) {
|
||||
return params;
|
||||
} else {
|
||||
|
@ -95,7 +95,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
|
||||
/// Adds hooks to this instance.
|
||||
void addHooks(Angel app) {
|
||||
var hooks = getAnnotation<Hooks>(inner, app.container.reflector);
|
||||
var hooks = getAnnotation<Hooks>(inner, app.container!.reflector);
|
||||
List<HookedServiceEventListener<Id, Data, T>> before = [], after = [];
|
||||
|
||||
if (hooks != null) {
|
||||
|
@ -105,8 +105,8 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
|
||||
void applyListeners(
|
||||
Function fn, HookedServiceEventDispatcher<Id, Data, T> dispatcher,
|
||||
[bool isAfter]) {
|
||||
Hooks hooks = getAnnotation<Hooks>(fn, app.container.reflector);
|
||||
[bool? isAfter]) {
|
||||
Hooks? hooks = getAnnotation<Hooks>(fn, app.container!.reflector);
|
||||
final listeners = <HookedServiceEventListener<Id, Data, T>>[]
|
||||
..addAll(isAfter == true ? after : before);
|
||||
|
||||
|
@ -140,7 +140,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
return true;
|
||||
});
|
||||
|
||||
void addRoutes([Service s]) {
|
||||
void addRoutes([Service? s]) {
|
||||
super.addRoutes(s ?? inner);
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<List<Data>> index([Map<String, dynamic> _params]) {
|
||||
Future<List<Data>> index([Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeIndexed
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -296,7 +296,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Data> read(Id id, [Map<String, dynamic> _params]) {
|
||||
Future<Data> read(Id? id, [Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeRead
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -322,7 +322,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Data> create(Data data, [Map<String, dynamic> _params]) {
|
||||
Future<Data> create(Data? data, [Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeCreated
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -337,7 +337,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
.then((after) => after.result as Data);
|
||||
}
|
||||
|
||||
return inner.create(before.data, params).then((result) {
|
||||
return inner.create(before.data!, params).then((result) {
|
||||
return afterCreated
|
||||
._emit(HookedServiceEvent(true, _getRequest(_params),
|
||||
_getResponse(_params), inner, HookedServiceEvent.created,
|
||||
|
@ -348,7 +348,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Data> modify(Id id, Data data, [Map<String, dynamic> _params]) {
|
||||
Future<Data> modify(Id? id, Data? data, [Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeModified
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -366,7 +366,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
.then((after) => after.result as Data);
|
||||
}
|
||||
|
||||
return inner.modify(id, before.data, params).then((result) {
|
||||
return inner.modify(id, before.data!, params).then((result) {
|
||||
return afterModified
|
||||
._emit(HookedServiceEvent(true, _getRequest(_params),
|
||||
_getResponse(_params), inner, HookedServiceEvent.created,
|
||||
|
@ -377,7 +377,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Data> update(Id id, Data data, [Map<String, dynamic> _params]) {
|
||||
Future<Data> update(Id? id, Data? data, [Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeUpdated
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -395,7 +395,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
.then((after) => after.result as Data);
|
||||
}
|
||||
|
||||
return inner.update(id, before.data, params).then((result) {
|
||||
return inner.update(id, before.data!, params).then((result) {
|
||||
return afterUpdated
|
||||
._emit(HookedServiceEvent(true, _getRequest(_params),
|
||||
_getResponse(_params), inner, HookedServiceEvent.updated,
|
||||
|
@ -406,7 +406,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Data> remove(Id id, [Map<String, dynamic> _params]) {
|
||||
Future<Data> remove(Id? id, [Map<String, dynamic>? _params]) {
|
||||
var params = _stripReq(_params);
|
||||
return beforeRemoved
|
||||
._emit(HookedServiceEvent(false, _getRequest(_params),
|
||||
|
@ -434,7 +434,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
/// Fires an `after` event. This will not be propagated to clients,
|
||||
/// but will be broadcasted to WebSockets, etc.
|
||||
Future<HookedServiceEvent<Id, Data, T>> fire(String eventName, result,
|
||||
[HookedServiceEventListener<Id, Data, T> callback]) {
|
||||
[HookedServiceEventListener<Id, Data, T>? callback]) {
|
||||
HookedServiceEventDispatcher<Id, Data, T> dispatcher;
|
||||
|
||||
switch (eventName) {
|
||||
|
@ -469,8 +469,8 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
Future<HookedServiceEvent<Id, Data, T>> fireEvent(
|
||||
HookedServiceEventDispatcher<Id, Data, T> dispatcher,
|
||||
HookedServiceEvent<Id, Data, T> event,
|
||||
[HookedServiceEventListener<Id, Data, T> callback]) {
|
||||
Future f;
|
||||
[HookedServiceEventListener<Id, Data, T>? callback]) {
|
||||
Future? f;
|
||||
if (callback != null && event?._canceled != true) {
|
||||
f = Future.sync(() => callback(event));
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
|
|||
}
|
||||
|
||||
/// Fired when a hooked service is invoked.
|
||||
class HookedServiceEvent<Id, Data, T extends Service<Id, Data>> {
|
||||
class HookedServiceEvent<Id, Data, T extends Service<Id?, Data?>> {
|
||||
static const String indexed = 'indexed';
|
||||
static const String read = 'read';
|
||||
static const String created = 'created';
|
||||
|
@ -506,38 +506,38 @@ class HookedServiceEvent<Id, Data, T extends Service<Id, Data>> {
|
|||
/// Resolves a service from the application.
|
||||
///
|
||||
/// Shorthand for `e.service.app.service(...)`.
|
||||
Service getService(Pattern path) => service.app.findService(path);
|
||||
Service? getService(Pattern path) => service.app!.findService(path);
|
||||
|
||||
bool _canceled = false;
|
||||
String _eventName;
|
||||
Id _id;
|
||||
Id? _id;
|
||||
bool _isAfter;
|
||||
Data data;
|
||||
Map<String, dynamic> _params;
|
||||
RequestContext _request;
|
||||
ResponseContext _response;
|
||||
Data? data;
|
||||
Map<String, dynamic>? _params;
|
||||
RequestContext? _request;
|
||||
ResponseContext? _response;
|
||||
var result;
|
||||
|
||||
String get eventName => _eventName;
|
||||
|
||||
Id get id => _id;
|
||||
Id? get id => _id;
|
||||
|
||||
bool get isAfter => _isAfter == true;
|
||||
|
||||
bool get isBefore => !isAfter;
|
||||
|
||||
Map get params => _params;
|
||||
Map? get params => _params;
|
||||
|
||||
RequestContext get request => _request;
|
||||
RequestContext? get request => _request;
|
||||
|
||||
ResponseContext get response => _response;
|
||||
ResponseContext? get response => _response;
|
||||
|
||||
/// The inner service whose method was hooked.
|
||||
T service;
|
||||
|
||||
HookedServiceEvent(this._isAfter, this._request, this._response, this.service,
|
||||
this._eventName,
|
||||
{Id id, this.data, Map<String, dynamic> params, this.result}) {
|
||||
{Id? id, this.data, Map<String, dynamic>? params, this.result}) {
|
||||
_id = id;
|
||||
_params = params ?? {};
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class HostnameSyntaxParser {
|
|||
|
||||
RegExp parse() {
|
||||
var b = StringBuffer();
|
||||
var parts = Queue<String>();
|
||||
var parts = Queue<String?>();
|
||||
|
||||
while (!_scanner.isDone) {
|
||||
if (_scanner.scan('|')) {
|
||||
|
@ -25,7 +25,7 @@ class HostnameSyntaxParser {
|
|||
throw _formatExc('No hostname parts found before "|".');
|
||||
} else {
|
||||
var next = _parseHostnamePart();
|
||||
if (next == null) {
|
||||
if (next.isEmpty) {
|
||||
throw _formatExc('No hostname parts found after "|".');
|
||||
} else {
|
||||
var prev = parts.removeLast();
|
||||
|
@ -33,11 +33,11 @@ class HostnameSyntaxParser {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
var part = _parseHostnamePart();
|
||||
if (part != null) {
|
||||
String part = _parseHostnamePart();
|
||||
if (part.isNotEmpty) {
|
||||
if (_scanner.scan('.')) {
|
||||
var subPart = _parseHostnamePart(shouldThrow: false);
|
||||
while (subPart != null) {
|
||||
while (subPart.isNotEmpty) {
|
||||
part += '\\.' + subPart;
|
||||
if (_scanner.scan('.')) {
|
||||
subPart = _parseHostnamePart(shouldThrow: false);
|
||||
|
@ -46,7 +46,6 @@ class HostnameSyntaxParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.add(part);
|
||||
}
|
||||
}
|
||||
|
@ -71,12 +70,12 @@ class HostnameSyntaxParser {
|
|||
} else if (_scanner.scan('+')) {
|
||||
return r'[^$]+';
|
||||
} else if (_scanner.scan(_safe)) {
|
||||
return _scanner.lastMatch[0];
|
||||
return _scanner.lastMatch?[0] ?? "";
|
||||
} else if (!_scanner.isDone && shouldThrow) {
|
||||
var s = String.fromCharCode(_scanner.peekChar());
|
||||
var s = String.fromCharCode(_scanner.peekChar()!);
|
||||
throw _formatExc('Unexpected character "$s".');
|
||||
} else {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,10 +60,10 @@ class HostnameRouter {
|
|||
Map<Pattern, FutureOr<void> Function(Angel)> configurers,
|
||||
{Reflector reflector = const EmptyReflector(),
|
||||
AngelEnvironment environment = angelEnv,
|
||||
Logger logger,
|
||||
Logger? logger,
|
||||
bool allowMethodOverrides = true,
|
||||
FutureOr<String> Function(dynamic) serializer,
|
||||
ViewGenerator viewGenerator}) {
|
||||
FutureOr<String> Function(dynamic)? serializer,
|
||||
ViewGenerator? viewGenerator}) {
|
||||
var creators = configurers.map((p, c) {
|
||||
return MapEntry(p, () async {
|
||||
var app = Angel(
|
||||
|
@ -89,17 +89,17 @@ class HostnameRouter {
|
|||
if (req.hostname != null) {
|
||||
for (var pattern in _patterns) {
|
||||
// print('${req.hostname} vs $_creators');
|
||||
if (pattern.allMatches(req.hostname).isNotEmpty) {
|
||||
if (pattern.allMatches(req.hostname!).isNotEmpty) {
|
||||
// Resolve the entire pipeline within the context of the selected app.
|
||||
var app = _apps[pattern] ??= (await _creators[pattern]());
|
||||
var app = _apps[pattern] ??= (await _creators[pattern]!());
|
||||
// print('App for ${req.hostname} = $app from $pattern');
|
||||
// app.dumpTree();
|
||||
|
||||
var r = app.optimizedRouter;
|
||||
var resolved = r.resolveAbsolute(req.path, method: req.method);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler>(resolved);
|
||||
var resolved = r.resolveAbsolute(req.path, method: req.method!);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler?>(resolved);
|
||||
// print('Pipeline: $pipeline');
|
||||
for (var handler in pipeline.handlers) {
|
||||
for (var handler in pipeline.handlers!) {
|
||||
// print(handler);
|
||||
// Avoid stack overflow.
|
||||
if (handler == handleRequest) {
|
||||
|
|
|
@ -8,13 +8,13 @@ const List<Type> _primitiveTypes = [String, int, num, double, Null];
|
|||
///
|
||||
/// Calling [ioc] also auto-serializes the result of a [handler].
|
||||
RequestHandler ioc(Function handler, {Iterable<String> optional = const []}) {
|
||||
InjectionRequest injection;
|
||||
RequestHandler contained;
|
||||
InjectionRequest? injection;
|
||||
RequestHandler? contained;
|
||||
|
||||
return (req, res) {
|
||||
if (injection == null) {
|
||||
injection = preInject(handler, req.app.container.reflector);
|
||||
injection.optional.addAll(optional ?? []);
|
||||
injection = preInject(handler, req.app.container!.reflector);
|
||||
injection!.optional.addAll(optional);
|
||||
contained = handleContained(handler, injection);
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,9 @@ RequestHandler ioc(Function handler, {Iterable<String> optional = const []}) {
|
|||
};
|
||||
}
|
||||
|
||||
resolveInjection(requirement, InjectionRequest injection, RequestContext req,
|
||||
resolveInjection(requirement, InjectionRequest? injection, RequestContext req,
|
||||
ResponseContext res, bool throwOnUnresolved,
|
||||
[Container container]) async {
|
||||
[Container? container]) async {
|
||||
var propFromApp;
|
||||
container ??= req?.container ?? res?.app?.container;
|
||||
|
||||
|
@ -33,20 +33,20 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
|
|||
} else if (requirement == ResponseContext) {
|
||||
return res;
|
||||
} else if (requirement is String &&
|
||||
injection.parameters.containsKey(requirement)) {
|
||||
var param = injection.parameters[requirement];
|
||||
injection!.parameters.containsKey(requirement)) {
|
||||
var param = injection.parameters[requirement]!;
|
||||
var value = param.getValue(req);
|
||||
if (value == null && param.required != false) throw param.error;
|
||||
if (value == null && param.required != false) throw param.error as Object;
|
||||
return value;
|
||||
} else if (requirement is String) {
|
||||
if (req.container.hasNamed(requirement)) {
|
||||
return req.container.findByName(requirement);
|
||||
if (req.container!.hasNamed(requirement)) {
|
||||
return req.container!.findByName(requirement);
|
||||
}
|
||||
if (req.params.containsKey(requirement)) {
|
||||
return req.params[requirement];
|
||||
} else if ((propFromApp = req.app.findProperty(requirement)) != null) {
|
||||
return propFromApp;
|
||||
} else if (injection.optional.contains(requirement)) {
|
||||
} else if (injection!.optional.contains(requirement)) {
|
||||
return null;
|
||||
} else if (throwOnUnresolved) {
|
||||
throw ArgumentError(
|
||||
|
@ -69,7 +69,7 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
|
|||
}
|
||||
} else if (requirement is Type && requirement != dynamic) {
|
||||
try {
|
||||
var futureType = container.reflector.reflectFutureOf(requirement);
|
||||
var futureType = container!.reflector.reflectFutureOf(requirement);
|
||||
if (container.has(futureType.reflectedType)) {
|
||||
return await container.make(futureType.reflectedType);
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
|
|||
// Ignore.
|
||||
}
|
||||
|
||||
return await container.make(requirement);
|
||||
return await container!.make(requirement);
|
||||
} else if (throwOnUnresolved) {
|
||||
throw ArgumentError(
|
||||
'$requirement cannot be injected into a request handler.');
|
||||
|
@ -95,10 +95,10 @@ bool suitableForInjection(
|
|||
}
|
||||
|
||||
/// Handles a request with a DI-enabled handler.
|
||||
RequestHandler handleContained(Function handler, InjectionRequest injection,
|
||||
[Container container]) {
|
||||
RequestHandler handleContained(Function? handler, InjectionRequest? injection,
|
||||
[Container? container]) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
if (injection.parameters.isNotEmpty &&
|
||||
if (injection!.parameters.isNotEmpty &&
|
||||
injection.parameters.values.any((p) => p.match != null) &&
|
||||
!suitableForInjection(req, res, injection)) return Future.value(true);
|
||||
|
||||
|
@ -116,7 +116,7 @@ RequestHandler handleContained(Function handler, InjectionRequest injection,
|
|||
[entry.key, entry.value], injection, req, res, false, container);
|
||||
}
|
||||
|
||||
return Function.apply(handler, args, named);
|
||||
return Function.apply(handler!, args, named);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ class InjectionRequest {
|
|||
InjectionRequest preInject(Function handler, Reflector reflector) {
|
||||
var injection = InjectionRequest();
|
||||
|
||||
var closureMirror = reflector.reflectFunction(handler);
|
||||
var closureMirror = reflector.reflectFunction(handler)!;
|
||||
|
||||
if (closureMirror.parameters.isEmpty) return injection;
|
||||
|
||||
|
@ -169,9 +169,8 @@ InjectionRequest preInject(Function handler, Reflector reflector) {
|
|||
var _Parameter = reflector.reflectType(Parameter);
|
||||
|
||||
var p = parameter.annotations
|
||||
.firstWhere((m) => m.type.isAssignableTo(_Parameter),
|
||||
orElse: () => null)
|
||||
?.reflectee as Parameter;
|
||||
.firstWhereOrNull((m) => m.type.isAssignableTo(_Parameter))
|
||||
?.reflectee as Parameter?;
|
||||
//print(p);
|
||||
if (p != null) {
|
||||
injection.parameters[name] = Parameter(
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:angel_http_exception/angel_http_exception.dart';
|
|||
import 'service.dart';
|
||||
|
||||
/// A basic service that manages an in-memory list of maps.
|
||||
class MapService extends Service<String, Map<String, dynamic>> {
|
||||
class MapService extends Service<String?, Map<String, dynamic>> {
|
||||
/// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`.
|
||||
///
|
||||
/// `false` by default.
|
||||
|
@ -48,14 +48,14 @@ class MapService extends Service<String, Map<String, dynamic>> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<List<Map<String, dynamic>>> index([Map<String, dynamic> params]) {
|
||||
Future<List<Map<String, dynamic>>> index([Map<String, dynamic>? params]) {
|
||||
if (allowQuery == false || params == null || params['query'] is! Map) {
|
||||
return Future.value(items);
|
||||
} else {
|
||||
var query = params['query'] as Map;
|
||||
var query = params['query'] as Map?;
|
||||
|
||||
return Future.value(items.where((item) {
|
||||
for (var key in query.keys) {
|
||||
for (var key in query!.keys) {
|
||||
if (!item.containsKey(key)) {
|
||||
return false;
|
||||
} else if (item[key] != query[key]) return false;
|
||||
|
@ -67,15 +67,15 @@ class MapService extends Service<String, Map<String, dynamic>> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> read(String id, [Map<String, dynamic> params]) {
|
||||
Future<Map<String, dynamic>> read(String? id, [Map<String, dynamic>? params]) {
|
||||
return Future.value(items.firstWhere(_matchesId(id),
|
||||
orElse: () => throw AngelHttpException.notFound(
|
||||
message: 'No record found for ID $id')));
|
||||
orElse: (() => throw AngelHttpException.notFound(
|
||||
message: 'No record found for ID $id')) as Map<String, dynamic> Function()?));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> create(Map<String, dynamic> data,
|
||||
[Map<String, dynamic> params]) {
|
||||
[Map<String, dynamic>? params]) {
|
||||
if (data is! Map) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message:
|
||||
|
@ -95,8 +95,8 @@ class MapService extends Service<String, Map<String, dynamic>> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> modify(String id, Map<String, dynamic> data,
|
||||
[Map<String, dynamic> params]) {
|
||||
Future<Map<String, dynamic>> modify(String? id, Map<String, dynamic> data,
|
||||
[Map<String, dynamic>? params]) {
|
||||
if (data is! Map) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message:
|
||||
|
@ -119,8 +119,8 @@ class MapService extends Service<String, Map<String, dynamic>> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data,
|
||||
[Map<String, dynamic> params]) {
|
||||
Future<Map<String, dynamic>> update(String? id, Map<String, dynamic> data,
|
||||
[Map<String, dynamic>? params]) {
|
||||
if (data is! Map) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message:
|
||||
|
@ -149,8 +149,8 @@ class MapService extends Service<String, Map<String, dynamic>> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> remove(String id,
|
||||
[Map<String, dynamic> params]) {
|
||||
Future<Map<String, dynamic>> remove(String? id,
|
||||
[Map<String, dynamic>? params]) {
|
||||
if (id == null || id == 'null') {
|
||||
// Remove everything...
|
||||
if (!(allowRemoveAll == true ||
|
||||
|
|
|
@ -45,9 +45,9 @@ const NoExpose noExpose = NoExpose();
|
|||
/// ```
|
||||
class Expose {
|
||||
final String method;
|
||||
final String path;
|
||||
final Iterable<RequestHandler> middleware;
|
||||
final String as;
|
||||
final String? path;
|
||||
final Iterable<RequestHandler>? middleware;
|
||||
final String? as;
|
||||
final List<String> allowNull;
|
||||
|
||||
static const Expose get = Expose(null, method: 'GET'),
|
||||
|
@ -71,16 +71,16 @@ class Expose {
|
|||
/// Used to apply special dependency injections or functionality to a function parameter.
|
||||
class Parameter {
|
||||
/// Inject the value of a request cookie.
|
||||
final String cookie;
|
||||
final String? cookie;
|
||||
|
||||
/// Inject the value of a request header.
|
||||
final String header;
|
||||
final String? header;
|
||||
|
||||
/// Inject the value of a key from the session.
|
||||
final String session;
|
||||
final String? session;
|
||||
|
||||
/// Inject the value of a key from the query.
|
||||
final String query;
|
||||
final String? query;
|
||||
|
||||
/// Only execute the handler if the value of this parameter matches the given value.
|
||||
final match;
|
||||
|
@ -89,7 +89,7 @@ class Parameter {
|
|||
final defaultValue;
|
||||
|
||||
/// If `true` (default), then an error will be thrown if this parameter is not present.
|
||||
final bool required;
|
||||
final bool? required;
|
||||
|
||||
const Parameter(
|
||||
{this.cookie,
|
||||
|
@ -122,17 +122,17 @@ class Parameter {
|
|||
/// Obtains a value for this parameter from a [RequestContext].
|
||||
getValue(RequestContext req) {
|
||||
if (cookie?.isNotEmpty == true) {
|
||||
return req.cookies.firstWhere((c) => c.name == cookie)?.value ??
|
||||
return req.cookies!.firstWhere((c) => c.name == cookie)?.value ??
|
||||
defaultValue;
|
||||
}
|
||||
if (header?.isNotEmpty == true) {
|
||||
return req.headers.value(header) ?? defaultValue;
|
||||
return req.headers!.value(header!) ?? defaultValue;
|
||||
}
|
||||
if (session?.isNotEmpty == true) {
|
||||
return req.session[session] ?? defaultValue;
|
||||
return req.session![session] ?? defaultValue;
|
||||
}
|
||||
if (query?.isNotEmpty == true) {
|
||||
return req.uri.queryParameters[query] ?? defaultValue;
|
||||
return req.uri!.queryParameters[query!] ?? defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:http_server/http_server.dart';
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'metadata.dart';
|
||||
import 'response_context.dart';
|
||||
|
@ -31,13 +32,13 @@ abstract class RequestContext<RawRequest> {
|
|||
/// when a [RequestContext] is done being processed.
|
||||
final List<FutureOr<void> Function()> shutdownHooks = [];
|
||||
|
||||
String _acceptHeaderCache, _extensionCache;
|
||||
bool _acceptsAllCache, _hasParsedBody = false, _closed = false;
|
||||
Map<String, dynamic> _bodyFields, _queryParameters;
|
||||
List _bodyList;
|
||||
Object _bodyObject;
|
||||
List<UploadedFile> _uploadedFiles;
|
||||
MediaType _contentType;
|
||||
String? _acceptHeaderCache, _extensionCache;
|
||||
bool? _acceptsAllCache, _hasParsedBody = false, _closed = false;
|
||||
Map<String?, dynamic>? _bodyFields, _queryParameters;
|
||||
List? _bodyList;
|
||||
Object? _bodyObject;
|
||||
List<UploadedFile>? _uploadedFiles;
|
||||
MediaType? _contentType;
|
||||
|
||||
/// The underlying [RawRequest] provided by the driver.
|
||||
RawRequest get rawRequest;
|
||||
|
@ -46,22 +47,22 @@ abstract class RequestContext<RawRequest> {
|
|||
final Map<String, dynamic> serviceParams = {};
|
||||
|
||||
/// The [Angel] instance that is responding to this request.
|
||||
Angel app;
|
||||
late Angel app;
|
||||
|
||||
/// Any cookies sent with this request.
|
||||
List<Cookie> get cookies;
|
||||
List<Cookie>? get cookies;
|
||||
|
||||
/// All HTTP headers sent with this request.
|
||||
HttpHeaders get headers;
|
||||
HttpHeaders? get headers;
|
||||
|
||||
/// The requested hostname.
|
||||
String get hostname;
|
||||
String? get hostname;
|
||||
|
||||
/// The IoC container that can be used to provide functionality to produce
|
||||
/// objects of a given type.
|
||||
///
|
||||
/// This is a *child* of the container found in `app`.
|
||||
Container get container;
|
||||
Container? get container;
|
||||
|
||||
/// The user's IP.
|
||||
String get ip => remoteAddress.address;
|
||||
|
@ -69,24 +70,24 @@ abstract class RequestContext<RawRequest> {
|
|||
/// This request's HTTP method.
|
||||
///
|
||||
/// This may have been processed by an override. See [originalMethod] to get the real method.
|
||||
String get method;
|
||||
String? get method;
|
||||
|
||||
/// The original HTTP verb sent to the server.
|
||||
String get originalMethod;
|
||||
String? get originalMethod;
|
||||
|
||||
/// The content type of an incoming request.
|
||||
MediaType get contentType =>
|
||||
_contentType ??= MediaType.parse(headers.contentType.toString());
|
||||
MediaType? get contentType =>
|
||||
_contentType ??= MediaType.parse(headers!.contentType.toString());
|
||||
|
||||
/// The URL parameters extracted from the request URI.
|
||||
Map<String, dynamic> params = <String, dynamic>{};
|
||||
Map<String?, dynamic> params = <String?, dynamic>{};
|
||||
|
||||
/// The requested path.
|
||||
String get path;
|
||||
String? get path;
|
||||
|
||||
/// Is this an **XMLHttpRequest**?
|
||||
bool get isXhr {
|
||||
return headers.value("X-Requested-With")?.trim()?.toLowerCase() ==
|
||||
return headers!.value("X-Requested-With")?.trim()?.toLowerCase() ==
|
||||
'xmlhttprequest';
|
||||
}
|
||||
|
||||
|
@ -94,22 +95,22 @@ abstract class RequestContext<RawRequest> {
|
|||
InternetAddress get remoteAddress;
|
||||
|
||||
/// The user's HTTP session.
|
||||
HttpSession get session;
|
||||
HttpSession? get session;
|
||||
|
||||
/// The [Uri] instance representing the path this request is responding to.
|
||||
Uri get uri;
|
||||
Uri? get uri;
|
||||
|
||||
/// The [Stream] of incoming binary data sent from the client.
|
||||
Stream<List<int>> get body;
|
||||
Stream<List<int>>? get body;
|
||||
|
||||
/// Returns `true` if [parseBody] has been called so far.
|
||||
bool get hasParsedBody => _hasParsedBody;
|
||||
bool? get hasParsedBody => _hasParsedBody;
|
||||
|
||||
/// Returns a *mutable* [Map] of the fields parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
Map<String, dynamic> get bodyAsMap {
|
||||
if (!hasParsedBody) {
|
||||
Map<String?, dynamic>? get bodyAsMap {
|
||||
if (!hasParsedBody!) {
|
||||
throw StateError('The request body has not been parsed yet.');
|
||||
} else if (_bodyFields == null) {
|
||||
throw StateError('The request body, $_bodyObject, is not a Map.');
|
||||
|
@ -121,13 +122,13 @@ abstract class RequestContext<RawRequest> {
|
|||
/// This setter allows you to explicitly set the request body **exactly once**.
|
||||
///
|
||||
/// Use this if the format of the body is not natively parsed by Angel.
|
||||
set bodyAsMap(Map<String, dynamic> value) => bodyAsObject = value;
|
||||
set bodyAsMap(Map<String?, dynamic>? value) => bodyAsObject = value;
|
||||
|
||||
/// Returns a *mutable* [List] parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
List get bodyAsList {
|
||||
if (!hasParsedBody) {
|
||||
List? get bodyAsList {
|
||||
if (!hasParsedBody!) {
|
||||
throw StateError('The request body has not been parsed yet.');
|
||||
} else if (_bodyList == null) {
|
||||
throw StateError('The request body, $_bodyObject, is not a List.');
|
||||
|
@ -139,13 +140,13 @@ abstract class RequestContext<RawRequest> {
|
|||
/// This setter allows you to explicitly set the request body **exactly once**.
|
||||
///
|
||||
/// Use this if the format of the body is not natively parsed by Angel.
|
||||
set bodyAsList(List value) => bodyAsObject = value;
|
||||
set bodyAsList(List? value) => bodyAsObject = value;
|
||||
|
||||
/// Returns the parsed request body, whatever it may be (typically a [Map] or [List]).
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
Object get bodyAsObject {
|
||||
if (!hasParsedBody) {
|
||||
Object? get bodyAsObject {
|
||||
if (!hasParsedBody!) {
|
||||
throw StateError('The request body has not been parsed yet.');
|
||||
}
|
||||
|
||||
|
@ -161,7 +162,7 @@ abstract class RequestContext<RawRequest> {
|
|||
'The request body has already been parsed/set, and cannot be overwritten.');
|
||||
} else {
|
||||
if (value is List) _bodyList = value;
|
||||
if (value is Map<String, dynamic>) _bodyFields = value;
|
||||
if (value is Map<String?, dynamic>) _bodyFields = value;
|
||||
_bodyObject = value;
|
||||
_hasParsedBody = true;
|
||||
}
|
||||
|
@ -170,8 +171,8 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Returns a *mutable* map of the files parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
List<UploadedFile> get uploadedFiles {
|
||||
if (!hasParsedBody) {
|
||||
List<UploadedFile>? get uploadedFiles {
|
||||
if (!hasParsedBody!) {
|
||||
throw StateError('The request body has not been parsed yet.');
|
||||
}
|
||||
|
||||
|
@ -179,13 +180,13 @@ abstract class RequestContext<RawRequest> {
|
|||
}
|
||||
|
||||
/// Returns a *mutable* map of the fields contained in the query.
|
||||
Map<String, dynamic> get queryParameters =>
|
||||
_queryParameters ??= Map<String, dynamic>.from(uri.queryParameters);
|
||||
Map<String?, dynamic> get queryParameters =>
|
||||
_queryParameters ??= Map<String?, dynamic>.from(uri!.queryParameters);
|
||||
|
||||
/// Returns the file extension of the requested path, if any.
|
||||
///
|
||||
/// Includes the leading `.`, if there is one.
|
||||
String get extension => _extensionCache ??= p.extension(uri.path);
|
||||
String get extension => _extensionCache ??= p.extension(uri!.path);
|
||||
|
||||
/// Returns `true` if the client's `Accept` header indicates that the given [contentType] is considered a valid response.
|
||||
///
|
||||
|
@ -207,14 +208,14 @@ abstract class RequestContext<RawRequest> {
|
|||
'RequestContext.accepts expects the `contentType` parameter to NOT be null.');
|
||||
}
|
||||
|
||||
_acceptHeaderCache ??= headers.value('accept');
|
||||
_acceptHeaderCache ??= headers!.value('accept');
|
||||
|
||||
if (_acceptHeaderCache == null) {
|
||||
return true;
|
||||
} else if (strict != true && _acceptHeaderCache.contains('*/*')) {
|
||||
} else if (strict != true && _acceptHeaderCache!.contains('*/*')) {
|
||||
return true;
|
||||
} else {
|
||||
return _acceptHeaderCache.contains(contentTypeString);
|
||||
return _acceptHeaderCache!.contains(contentTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,14 +223,14 @@ abstract class RequestContext<RawRequest> {
|
|||
bool get acceptsAll => _acceptsAllCache ??= accepts('*/*');
|
||||
|
||||
/// Shorthand for deserializing [bodyAsMap], using some transformer function [f].
|
||||
Future<T> deserializeBody<T>(FutureOr<T> Function(Map) f,
|
||||
Future<T> deserializeBody<T>(FutureOr<T> Function(Map?) f,
|
||||
{Encoding encoding = utf8}) async {
|
||||
await parseBody(encoding: encoding);
|
||||
return await f(bodyAsMap);
|
||||
}
|
||||
|
||||
/// Shorthand for decoding [bodyAsMap], using some [codec].
|
||||
Future<T> decodeBody<T>(Codec<T, Map> codec, {Encoding encoding = utf8}) =>
|
||||
Future<T> decodeBody<T>(Codec<T, Map?> codec, {Encoding encoding = utf8}) =>
|
||||
deserializeBody(codec.decode, encoding: encoding);
|
||||
|
||||
/// Manually parses the request body, if it has not already been parsed.
|
||||
|
@ -238,47 +239,48 @@ abstract class RequestContext<RawRequest> {
|
|||
throw FormatException('Missing "content-type" header.');
|
||||
}
|
||||
|
||||
if (!_hasParsedBody) {
|
||||
if (!_hasParsedBody!) {
|
||||
_hasParsedBody = true;
|
||||
|
||||
if (contentType.type == 'application' && contentType.subtype == 'json') {
|
||||
if (contentType!.type == 'application' &&
|
||||
contentType!.subtype == 'json') {
|
||||
_uploadedFiles = [];
|
||||
|
||||
var parsed = _bodyObject =
|
||||
await encoding.decoder.bind(body).join().then(json.decode);
|
||||
var parsed = (_bodyObject =
|
||||
await encoding.decoder.bind(body!).join().then(json.decode))!;
|
||||
|
||||
if (parsed is Map) {
|
||||
_bodyFields = Map<String, dynamic>.from(parsed);
|
||||
_bodyFields = Map<String?, dynamic>.from(parsed);
|
||||
} else if (parsed is List) {
|
||||
_bodyList = parsed;
|
||||
}
|
||||
} else if (contentType.type == 'application' &&
|
||||
contentType.subtype == 'x-www-form-urlencoded') {
|
||||
} else if (contentType!.type == 'application' &&
|
||||
contentType!.subtype == 'x-www-form-urlencoded') {
|
||||
_uploadedFiles = [];
|
||||
var parsed = await encoding.decoder
|
||||
.bind(body)
|
||||
.bind(body!)
|
||||
.join()
|
||||
.then((s) => Uri.splitQueryString(s, encoding: encoding));
|
||||
_bodyFields = Map<String, dynamic>.from(parsed);
|
||||
} else if (contentType.type == 'multipart' &&
|
||||
contentType.subtype == 'form-data' &&
|
||||
contentType.parameters.containsKey('boundary')) {
|
||||
var boundary = contentType.parameters['boundary'];
|
||||
_bodyFields = Map<String?, dynamic>.from(parsed);
|
||||
} else if (contentType!.type == 'multipart' &&
|
||||
contentType!.subtype == 'form-data' &&
|
||||
contentType!.parameters.containsKey('boundary')) {
|
||||
var boundary = contentType!.parameters['boundary']!;
|
||||
var transformer = MimeMultipartTransformer(boundary);
|
||||
var parts = transformer.bind(body).map((part) =>
|
||||
var parts = transformer.bind(body!).map((part) =>
|
||||
HttpMultipartFormData.parse(part, defaultEncoding: encoding));
|
||||
_bodyFields = {};
|
||||
_uploadedFiles = [];
|
||||
|
||||
await for (var part in parts) {
|
||||
if (part.isBinary) {
|
||||
_uploadedFiles.add(UploadedFile(part));
|
||||
_uploadedFiles!.add(UploadedFile(part));
|
||||
} else if (part.isText &&
|
||||
part.contentDisposition.parameters.containsKey('name')) {
|
||||
// If there is no name, then don't parse it.
|
||||
var key = part.contentDisposition.parameters['name'];
|
||||
var value = await part.join();
|
||||
_bodyFields[key] = value;
|
||||
_bodyFields![key] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -291,13 +293,13 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Disposes of all resources.
|
||||
@mustCallSuper
|
||||
Future<void> close() async {
|
||||
if (!_closed) {
|
||||
if (!_closed!) {
|
||||
_closed = true;
|
||||
_acceptsAllCache = null;
|
||||
_acceptHeaderCache = null;
|
||||
serviceParams.clear();
|
||||
params.clear();
|
||||
await Future.forEach(shutdownHooks, (hook) => hook());
|
||||
await Future.forEach(shutdownHooks, (dynamic hook) => hook());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +309,7 @@ class UploadedFile {
|
|||
/// The underlying `form-data` item.
|
||||
final HttpMultipartFormData formData;
|
||||
|
||||
MediaType _contentType;
|
||||
MediaType? _contentType;
|
||||
|
||||
UploadedFile(this.formData);
|
||||
|
||||
|
@ -316,22 +318,22 @@ class UploadedFile {
|
|||
|
||||
/// The filename associated with the data on the user's system.
|
||||
/// Returns [:null:] if not present.
|
||||
String get filename => formData.contentDisposition.parameters['filename'];
|
||||
String? get filename => formData.contentDisposition.parameters['filename'];
|
||||
|
||||
/// The name of the field associated with this data.
|
||||
/// Returns [:null:] if not present.
|
||||
String get name => formData.contentDisposition.parameters['name'];
|
||||
String? get name => formData.contentDisposition.parameters['name'];
|
||||
|
||||
/// The parsed [:Content-Type:] header of the [:HttpMultipartFormData:].
|
||||
/// Returns [:null:] if not present.
|
||||
MediaType get contentType => _contentType ??= (formData.contentType == null
|
||||
MediaType? get contentType => _contentType ??= (formData.contentType == null
|
||||
? null
|
||||
: MediaType.parse(formData.contentType.toString()));
|
||||
|
||||
/// The parsed [:Content-Transfer-Encoding:] header of the
|
||||
/// [:HttpMultipartFormData:]. This field is used to determine how to decode
|
||||
/// the data. Returns [:null:] if not present.
|
||||
HeaderValue get contentTransferEncoding => formData.contentTransferEncoding;
|
||||
HeaderValue? get contentTransferEncoding => formData.contentTransferEncoding;
|
||||
|
||||
/// Reads the contents of the file into a single linear buffer.
|
||||
///
|
||||
|
|
|
@ -26,14 +26,14 @@ abstract class ResponseContext<RawResponse>
|
|||
'server': 'angel',
|
||||
});
|
||||
|
||||
Completer _done;
|
||||
Completer? _done;
|
||||
int _statusCode = 200;
|
||||
|
||||
/// The [Angel] instance that is sending a response.
|
||||
Angel app;
|
||||
Angel? app;
|
||||
|
||||
/// Is `Transfer-Encoding` chunked?
|
||||
bool chunked;
|
||||
bool? chunked;
|
||||
|
||||
/// Any and all cookies to be sent to the user.
|
||||
final List<Cookie> cookies = [];
|
||||
|
@ -49,7 +49,7 @@ abstract class ResponseContext<RawResponse>
|
|||
final Map<String, dynamic> renderParams = {};
|
||||
|
||||
/// Points to the [RequestContext] corresponding to this response.
|
||||
RequestContext get correspondingRequest;
|
||||
RequestContext? get correspondingRequest;
|
||||
|
||||
@override
|
||||
Future get done => (_done ?? Completer()).future;
|
||||
|
@ -71,12 +71,12 @@ abstract class ResponseContext<RawResponse>
|
|||
/// ```dart
|
||||
/// app.injectSerializer(JSON.encode);
|
||||
/// ```
|
||||
FutureOr<String> Function(dynamic) serializer = c.json.encode;
|
||||
FutureOr<String> Function(dynamic)? serializer = c.json.encode;
|
||||
|
||||
/// This response's status code.
|
||||
int get statusCode => _statusCode;
|
||||
|
||||
set statusCode(int value) {
|
||||
set statusCode(int? value) {
|
||||
if (!isOpen) {
|
||||
throw closed();
|
||||
} else {
|
||||
|
@ -94,7 +94,7 @@ abstract class ResponseContext<RawResponse>
|
|||
bool get isBuffered;
|
||||
|
||||
/// A set of UTF-8 encoded bytes that will be written to the response.
|
||||
BytesBuilder get buffer;
|
||||
BytesBuilder? get buffer;
|
||||
|
||||
/// The underlying [RawResponse] under this instance.
|
||||
RawResponse get rawResponse;
|
||||
|
@ -108,14 +108,14 @@ abstract class ResponseContext<RawResponse>
|
|||
/// Gets or sets the content length to send back to a client.
|
||||
///
|
||||
/// Returns `null` if the header is invalidly formatted.
|
||||
int get contentLength {
|
||||
return int.tryParse(headers['content-length']);
|
||||
int? get contentLength {
|
||||
return int.tryParse(headers['content-length']!);
|
||||
}
|
||||
|
||||
/// Gets or sets the content length to send back to a client.
|
||||
///
|
||||
/// If [value] is `null`, then the header will be removed.
|
||||
set contentLength(int value) {
|
||||
set contentLength(int? value) {
|
||||
if (value == null) {
|
||||
headers.remove('content-length');
|
||||
} else {
|
||||
|
@ -126,7 +126,7 @@ abstract class ResponseContext<RawResponse>
|
|||
/// Gets or sets the content type to send back to a client.
|
||||
MediaType get contentType {
|
||||
try {
|
||||
return MediaType.parse(headers['content-type']);
|
||||
return MediaType.parse(headers['content-type']!);
|
||||
} catch (_) {
|
||||
return MediaType('text', 'plain');
|
||||
}
|
||||
|
@ -140,18 +140,18 @@ abstract class ResponseContext<RawResponse>
|
|||
static StateError closed() => StateError('Cannot modify a closed response.');
|
||||
|
||||
/// Sends a download as a response.
|
||||
Future<void> download(File file, {String filename}) async {
|
||||
Future<void> download(File file, {String? filename}) async {
|
||||
if (!isOpen) throw closed();
|
||||
|
||||
headers["Content-Disposition"] =
|
||||
'attachment; filename="${filename ?? file.path}"';
|
||||
contentType = MediaType.parse(lookupMimeType(file.path));
|
||||
contentType = MediaType.parse(lookupMimeType(file.path)!);
|
||||
headers['content-length'] = file.lengthSync().toString();
|
||||
|
||||
if (!isBuffered) {
|
||||
await file.openRead().cast<List<int>>().pipe(this);
|
||||
} else {
|
||||
buffer.add(file.readAsBytesSync());
|
||||
buffer!.add(file.readAsBytesSync());
|
||||
await close();
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ abstract class ResponseContext<RawResponse>
|
|||
(buffer as LockableBytesBuilder).lock();
|
||||
}
|
||||
|
||||
if (_done?.isCompleted == false) _done.complete();
|
||||
if (_done?.isCompleted == false) _done!.complete();
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
|
@ -175,18 +175,18 @@ abstract class ResponseContext<RawResponse>
|
|||
///
|
||||
/// You can override the [contentType] sent; by default it is `application/javascript`.
|
||||
Future<void> jsonp(value,
|
||||
{String callbackName = "callback", MediaType contentType}) {
|
||||
{String callbackName = "callback", MediaType? contentType}) {
|
||||
if (!isOpen) throw closed();
|
||||
this.contentType = contentType ?? MediaType('application', 'javascript');
|
||||
write("$callbackName(${serializer(value)})");
|
||||
write("$callbackName(${serializer!(value)})");
|
||||
return close();
|
||||
}
|
||||
|
||||
/// Renders a view to the response stream, and closes the response.
|
||||
Future<void> render(String view, [Map<String, dynamic> data]) {
|
||||
Future<void> render(String view, [Map<String, dynamic>? data]) {
|
||||
if (!isOpen) throw closed();
|
||||
contentType = MediaType('text', 'html', {'charset': 'utf-8'});
|
||||
return Future<String>.sync(() => app.viewGenerator(
|
||||
return Future<String>.sync(() => app!.viewGenerator!(
|
||||
view,
|
||||
Map<String, dynamic>.from(renderParams)
|
||||
..addAll(data ?? <String, dynamic>{}))).then((content) {
|
||||
|
@ -202,13 +202,13 @@ abstract class ResponseContext<RawResponse>
|
|||
/// based on the provided params.
|
||||
///
|
||||
/// See [Router]#navigate for more. :)
|
||||
Future<void> redirect(url, {bool absolute = true, int code = 302}) {
|
||||
Future<void> redirect(url, {bool absolute = true, int? code = 302}) {
|
||||
if (!isOpen) throw closed();
|
||||
headers
|
||||
..['content-type'] = 'text/html'
|
||||
..['location'] = (url is String || url is Uri)
|
||||
? url.toString()
|
||||
: app.navigate(url as Iterable, absolute: absolute);
|
||||
: app!.navigate(url as Iterable, absolute: absolute);
|
||||
statusCode = code ?? 302;
|
||||
write('''
|
||||
<!DOCTYPE html>
|
||||
|
@ -231,9 +231,9 @@ abstract class ResponseContext<RawResponse>
|
|||
}
|
||||
|
||||
/// Redirects to the given named [Route].
|
||||
Future<void> redirectTo(String name, [Map params, int code]) async {
|
||||
Future<void> redirectTo(String name, [Map? params, int? code]) async {
|
||||
if (!isOpen) throw closed();
|
||||
Route _findRoute(Router r) {
|
||||
Route? _findRoute(Router r) {
|
||||
for (Route route in r.routes) {
|
||||
if (route is SymlinkRoute) {
|
||||
final m = _findRoute(route.router);
|
||||
|
@ -245,11 +245,11 @@ abstract class ResponseContext<RawResponse>
|
|||
return null;
|
||||
}
|
||||
|
||||
Route matched = _findRoute(app);
|
||||
Route? matched = _findRoute(app!);
|
||||
|
||||
if (matched != null) {
|
||||
await redirect(
|
||||
matched.makeUri(params.keys.fold<Map<String, dynamic>>({}, (out, k) {
|
||||
matched.makeUri(params!.keys.fold<Map<String, dynamic>>({}, (out, k) {
|
||||
return out..[k.toString()] = params[k];
|
||||
})),
|
||||
code: code);
|
||||
|
@ -260,7 +260,7 @@ abstract class ResponseContext<RawResponse>
|
|||
}
|
||||
|
||||
/// Redirects to the given [Controller] action.
|
||||
Future<void> redirectToAction(String action, [Map params, int code]) {
|
||||
Future<void> redirectToAction(String action, [Map? params, int? code]) {
|
||||
if (!isOpen) throw closed();
|
||||
// UserController@show
|
||||
List<String> split = action.split("@");
|
||||
|
@ -270,14 +270,14 @@ abstract class ResponseContext<RawResponse>
|
|||
"Controller redirects must take the form of 'Controller@action'. You gave: $action");
|
||||
}
|
||||
|
||||
Controller controller =
|
||||
app.controllers[split[0].replaceAll(_straySlashes, '')];
|
||||
Controller? controller =
|
||||
app!.controllers[split[0].replaceAll(_straySlashes, '')];
|
||||
|
||||
if (controller == null) {
|
||||
throw Exception("Could not find a controller named '${split[0]}'");
|
||||
}
|
||||
|
||||
Route matched = controller.routeMappings[split[1]];
|
||||
Route? matched = controller.routeMappings[split[1]];
|
||||
|
||||
if (matched == null) {
|
||||
throw Exception(
|
||||
|
@ -285,24 +285,26 @@ abstract class ResponseContext<RawResponse>
|
|||
}
|
||||
|
||||
final head = controller
|
||||
.findExpose(app.container.reflector)
|
||||
.findExpose(app!.container!.reflector)!
|
||||
.path
|
||||
.toString()
|
||||
.replaceAll(_straySlashes, '');
|
||||
final tail = matched
|
||||
.makeUri(params.keys.fold<Map<String, dynamic>>({}, (out, k) {
|
||||
return out..[k.toString()] = params[k];
|
||||
}))
|
||||
.replaceAll(_straySlashes, '');
|
||||
|
||||
String tail = "";
|
||||
if (params != null) {
|
||||
tail = matched
|
||||
.makeUri(params.keys.fold<Map<String, dynamic>>({}, (out, k) {
|
||||
return out..[k.toString()] = params[k];
|
||||
}))
|
||||
.replaceAll(_straySlashes, '');
|
||||
}
|
||||
return redirect('$head/$tail'.replaceAll(_straySlashes, ''), code: code);
|
||||
}
|
||||
|
||||
/// Serializes data to the response.
|
||||
Future<bool> serialize(value, {MediaType contentType}) async {
|
||||
Future<bool> serialize(value, {MediaType? contentType}) async {
|
||||
if (!isOpen) throw closed();
|
||||
this.contentType = contentType ?? MediaType('application', 'json');
|
||||
var text = await serializer(value);
|
||||
var text = await serializer!(value);
|
||||
if (text.isEmpty) return true;
|
||||
write(text);
|
||||
await close();
|
||||
|
@ -314,13 +316,13 @@ abstract class ResponseContext<RawResponse>
|
|||
/// `HEAD` responses will not actually write data.
|
||||
Future streamFile(File file) async {
|
||||
if (!isOpen) throw closed();
|
||||
var mimeType = app.mimeTypeResolver.lookup(file.path);
|
||||
var mimeType = app!.mimeTypeResolver.lookup(file.path);
|
||||
contentLength = await file.length();
|
||||
contentType = mimeType == null
|
||||
? MediaType('application', 'octet-stream')
|
||||
: MediaType.parse(mimeType);
|
||||
|
||||
if (correspondingRequest.method != 'HEAD') {
|
||||
if (correspondingRequest!.method != 'HEAD') {
|
||||
return this
|
||||
.addStream(file.openRead().cast<List<int>>())
|
||||
.then((_) => this.close());
|
||||
|
@ -338,16 +340,16 @@ abstract class ResponseContext<RawResponse>
|
|||
Future addStream(Stream<List<int>> stream);
|
||||
|
||||
@override
|
||||
void addError(Object error, [StackTrace stackTrace]) {
|
||||
void addError(Object error, [StackTrace? stackTrace]) {
|
||||
if (_done?.isCompleted == false) {
|
||||
_done.completeError(error, stackTrace);
|
||||
_done!.completeError(error, stackTrace);
|
||||
} else if (_done == null) {
|
||||
Zone.current.handleUncaughtError(error, stackTrace);
|
||||
Zone.current.handleUncaughtError(error, stackTrace!);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes data to the response.
|
||||
void write(value, {Encoding encoding}) {
|
||||
void write(value, {Encoding? encoding}) {
|
||||
encoding ??= utf8;
|
||||
|
||||
if (!isOpen && isBuffered) {
|
||||
|
@ -355,7 +357,7 @@ abstract class ResponseContext<RawResponse>
|
|||
} else if (!isBuffered) {
|
||||
add(encoding.encode(value.toString()));
|
||||
} else {
|
||||
buffer.add(encoding.encode(value.toString()));
|
||||
buffer!.add(encoding.encode(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,12 +368,12 @@ abstract class ResponseContext<RawResponse>
|
|||
} else if (!isBuffered) {
|
||||
add([charCode]);
|
||||
} else {
|
||||
buffer.addByte(charCode);
|
||||
buffer!.addByte(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writeln([Object obj = ""]) {
|
||||
void writeln([Object? obj = ""]) {
|
||||
write(obj.toString());
|
||||
write('\r\n');
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ typedef FutureOr RequestHandler(RequestContext req, ResponseContext res);
|
|||
/// return `true`. Works well with [Router].chain.
|
||||
RequestHandler chain(Iterable<RequestHandler> handlers) {
|
||||
return (req, res) {
|
||||
Future Function() runPipeline;
|
||||
Future Function()? runPipeline;
|
||||
|
||||
for (var handler in handlers) {
|
||||
if (handler == null) break;
|
||||
|
@ -42,19 +42,19 @@ RequestHandler chain(Iterable<RequestHandler> handlers) {
|
|||
}
|
||||
|
||||
/// A routable server that can handle dynamic requests.
|
||||
class Routable extends Router<RequestHandler> {
|
||||
class Routable extends Router<RequestHandler?> {
|
||||
final Map<Pattern, Service> _services = {};
|
||||
final Map<Pattern, Service> _serviceLookups = {};
|
||||
final Map<Pattern, Service?> _serviceLookups = {};
|
||||
final Map configuration = {};
|
||||
|
||||
final Container _container;
|
||||
final Container? _container;
|
||||
|
||||
Routable([Reflector reflector])
|
||||
Routable([Reflector? reflector])
|
||||
: _container = reflector == null ? null : Container(reflector),
|
||||
super();
|
||||
|
||||
/// A [Container] used to inject dependencies.
|
||||
Container get container => _container;
|
||||
Container? get container => _container;
|
||||
|
||||
void close() {
|
||||
_services.clear();
|
||||
|
@ -73,34 +73,34 @@ class Routable extends Router<RequestHandler> {
|
|||
Stream<Service> get onService => _onService.stream;
|
||||
|
||||
/// Retrieves the service assigned to the given path.
|
||||
T findService<T extends Service>(Pattern path) {
|
||||
T? findService<T extends Service?>(Pattern path) {
|
||||
return _serviceLookups.putIfAbsent(path, () {
|
||||
return _services[path] ??
|
||||
_services[path.toString().replaceAll(_straySlashes, '')];
|
||||
}) as T;
|
||||
}) as T?;
|
||||
}
|
||||
|
||||
/// Shorthand for finding a [Service] in a statically-typed manner.
|
||||
Service<Id, Data> findServiceOf<Id, Data>(Pattern path) {
|
||||
Service<Id, Data>? findServiceOf<Id, Data>(Pattern path) {
|
||||
return findService<Service<Id, Data>>(path);
|
||||
}
|
||||
|
||||
/// Shorthand for finding a [HookedService] in a statically-typed manner.
|
||||
HookedService<dynamic, dynamic, T> findHookedService<T extends Service>(
|
||||
HookedService<dynamic, dynamic, T>? findHookedService<T extends Service>(
|
||||
Pattern path) {
|
||||
return findService(path) as HookedService<dynamic, dynamic, T>;
|
||||
return findService(path) as HookedService<dynamic, dynamic, T>?;
|
||||
}
|
||||
|
||||
@override
|
||||
Route<RequestHandler> addRoute(
|
||||
String method, String path, RequestHandler handler,
|
||||
{Iterable<RequestHandler> middleware}) {
|
||||
Route<RequestHandler?> addRoute(
|
||||
String? method, String? path, RequestHandler? handler,
|
||||
{Iterable<RequestHandler?>? middleware}) {
|
||||
middleware ??= [];
|
||||
final handlers = <RequestHandler>[];
|
||||
// Merge @Middleware declaration, if any
|
||||
var reflector = _container?.reflector;
|
||||
if (reflector != null && reflector is! ThrowingReflector) {
|
||||
Middleware middlewareDeclaration =
|
||||
Middleware? middlewareDeclaration =
|
||||
getAnnotation<Middleware>(handler, _container?.reflector);
|
||||
if (middlewareDeclaration != null) {
|
||||
handlers.addAll(middlewareDeclaration.handlers);
|
||||
|
@ -108,7 +108,7 @@ class Routable extends Router<RequestHandler> {
|
|||
}
|
||||
|
||||
final handlerSequence = <RequestHandler>[];
|
||||
handlerSequence.addAll(middleware ?? []);
|
||||
handlerSequence.addAll(middleware as Iterable<FutureOr<dynamic>? Function(RequestContext<dynamic>, ResponseContext<dynamic>)>? ?? []);
|
||||
handlerSequence.addAll(handlers);
|
||||
|
||||
return super.addRoute(method, path.toString(), handler,
|
||||
|
|
|
@ -26,21 +26,21 @@ typedef FutureOr<void> AngelConfigurer(Angel app);
|
|||
|
||||
/// A function that asynchronously generates a view from the given path and data.
|
||||
typedef FutureOr<String> ViewGenerator(String path,
|
||||
[Map<String, dynamic> data]);
|
||||
[Map<String, dynamic>? data]);
|
||||
|
||||
/// A powerful real-time/REST/MVC server class.
|
||||
class Angel extends Routable {
|
||||
static ViewGenerator noViewEngineConfigured =
|
||||
(String view, [Map data]) => 'No view engine has been configured yet.';
|
||||
(String view, [Map? data]) => 'No view engine has been configured yet.';
|
||||
|
||||
final List<Angel> _children = [];
|
||||
final Map<
|
||||
String,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
|
||||
Tuple4<List?, Map<String?, dynamic>, ParseResult<RouteResult?>?,
|
||||
MiddlewarePipeline>> handlerCache = HashMap();
|
||||
|
||||
Router<RequestHandler> _flattened;
|
||||
Angel _parent;
|
||||
Router<RequestHandler?>? _flattened;
|
||||
Angel? _parent;
|
||||
|
||||
/// A global Map of converters that can transform responses bodies.
|
||||
final Map<String, Converter<List<int>, List<int>>> encoders = {};
|
||||
|
@ -51,7 +51,7 @@ class Angel extends Routable {
|
|||
final MimeTypeResolver mimeTypeResolver = MimeTypeResolver();
|
||||
|
||||
/// A middleware to inject a serialize on every request.
|
||||
FutureOr<String> Function(dynamic) serializer;
|
||||
FutureOr<String> Function(dynamic)? serializer;
|
||||
|
||||
/// A [Map] of dependency data obtained via reflection.
|
||||
///
|
||||
|
@ -59,7 +59,7 @@ class Angel extends Routable {
|
|||
Map<dynamic, InjectionRequest> get preContained => _preContained;
|
||||
|
||||
/// Returns the [flatten]ed version of this router in production.
|
||||
Router<RequestHandler> get optimizedRouter => _flattened ?? this;
|
||||
Router<RequestHandler?> get optimizedRouter => _flattened ?? this;
|
||||
|
||||
/// Determines whether to allow HTTP request method overrides.
|
||||
bool allowMethodOverrides = true;
|
||||
|
@ -67,10 +67,10 @@ class Angel extends Routable {
|
|||
/// All child application mounted on this instance.
|
||||
List<Angel> get children => List<Angel>.unmodifiable(_children);
|
||||
|
||||
final Map<Pattern, Controller> _controllers = {};
|
||||
final Map<Pattern?, Controller> _controllers = {};
|
||||
|
||||
/// A set of [Controller] objects that have been loaded into the application.
|
||||
Map<Pattern, Controller> get controllers => _controllers;
|
||||
Map<Pattern?, Controller> get controllers => _controllers;
|
||||
|
||||
/// Now *deprecated*, in favor of [AngelEnv] and [angelEnv]. Use `app.environment.isProduction`
|
||||
/// instead.
|
||||
|
@ -91,10 +91,10 @@ class Angel extends Routable {
|
|||
final AngelEnvironment environment;
|
||||
|
||||
/// Returns the parent instance of this application, if any.
|
||||
Angel get parent => _parent;
|
||||
Angel? get parent => _parent;
|
||||
|
||||
/// Outputs diagnostics and debug messages.
|
||||
Logger logger;
|
||||
Logger? logger;
|
||||
|
||||
/// Plug-ins to be called right before server startup.
|
||||
///
|
||||
|
@ -121,7 +121,7 @@ class Angel extends Routable {
|
|||
/// A function that renders views.
|
||||
///
|
||||
/// Called by [ResponseContext]@`render`.
|
||||
ViewGenerator viewGenerator = noViewEngineConfigured;
|
||||
ViewGenerator? viewGenerator = noViewEngineConfigured;
|
||||
|
||||
/// The handler currently configured to run on [AngelHttpException]s.
|
||||
Function(AngelHttpException e, RequestContext req, ResponseContext res)
|
||||
|
@ -148,9 +148,9 @@ class Angel extends Routable {
|
|||
};
|
||||
|
||||
@override
|
||||
Route<RequestHandler> addRoute(
|
||||
String method, String path, RequestHandler handler,
|
||||
{Iterable<RequestHandler> middleware}) {
|
||||
Route<RequestHandler?> addRoute(
|
||||
String? method, String? path, RequestHandler? handler,
|
||||
{Iterable<RequestHandler?>? middleware}) {
|
||||
middleware ??= [];
|
||||
if (_flattened != null) {
|
||||
logger?.warning(
|
||||
|
@ -163,7 +163,7 @@ class Angel extends Routable {
|
|||
}
|
||||
|
||||
@override
|
||||
mount(String path, Router<RequestHandler> router) {
|
||||
mount(String path, Router<RequestHandler?> router) {
|
||||
if (_flattened != null) {
|
||||
logger?.warning(
|
||||
'WARNING: You added mounted a child router ($path) on the router, after it had been optimized.');
|
||||
|
@ -182,12 +182,12 @@ class Angel extends Routable {
|
|||
/// Loads some base dependencies into the service container.
|
||||
void bootstrapContainer() {
|
||||
if (runtimeType != Angel) {
|
||||
container.registerSingleton(this);
|
||||
container!.registerSingleton(this);
|
||||
}
|
||||
|
||||
container.registerSingleton<Angel>(this);
|
||||
container.registerSingleton<Routable>(this);
|
||||
container.registerSingleton<Router>(this);
|
||||
container!.registerSingleton<Angel>(this);
|
||||
container!.registerSingleton<Routable>(this);
|
||||
container!.registerSingleton<Router>(this);
|
||||
}
|
||||
|
||||
/// Shuts down the server, and closes any open [StreamController]s.
|
||||
|
@ -216,14 +216,14 @@ class Angel extends Routable {
|
|||
|
||||
@override
|
||||
void dumpTree(
|
||||
{callback(String tree),
|
||||
{callback(String tree)?,
|
||||
String header = 'Dumping route tree:',
|
||||
String tab = ' ',
|
||||
bool showMatchers = false}) {
|
||||
if (environment.isProduction) {
|
||||
_flattened ??= flatten(this);
|
||||
|
||||
_flattened.dumpTree(
|
||||
_flattened!.dumpTree(
|
||||
callback: callback,
|
||||
header: header?.isNotEmpty == true
|
||||
? header
|
||||
|
@ -284,7 +284,7 @@ class Angel extends Routable {
|
|||
/// Attempts to find a property by the given name within this application.
|
||||
findProperty(key) {
|
||||
if (configuration.containsKey(key)) return configuration[key];
|
||||
return parent != null ? parent.findProperty(key) : null;
|
||||
return parent != null ? parent!.findProperty(key) : null;
|
||||
}
|
||||
|
||||
/// Runs several optimizations, *if* [angelEnv.isProduction] is `true`.
|
||||
|
@ -305,7 +305,7 @@ class Angel extends Routable {
|
|||
/// If this function has been reflected before, then
|
||||
/// the execution will be faster, as the injection requirements were stored beforehand.
|
||||
Future runContained(Function handler, RequestContext req, ResponseContext res,
|
||||
[Container container]) {
|
||||
[Container? container]) {
|
||||
return Future.sync(() {
|
||||
if (_preContained.containsKey(handler)) {
|
||||
return handleContained(handler, _preContained[handler], container)(
|
||||
|
@ -318,11 +318,11 @@ class Angel extends Routable {
|
|||
|
||||
/// Runs with DI, and *always* reflects. Prefer [runContained].
|
||||
Future runReflected(Function handler, RequestContext req, ResponseContext res,
|
||||
[Container container]) {
|
||||
[Container? container]) {
|
||||
container ??= req?.container ?? res?.app?.container;
|
||||
var h = handleContained(
|
||||
handler,
|
||||
_preContained[handler] = preInject(handler, container.reflector),
|
||||
_preContained[handler] = preInject(handler, container!.reflector),
|
||||
container);
|
||||
return Future.sync(() => h(req, res));
|
||||
// return closureMirror.apply(args).reflectee;
|
||||
|
@ -340,13 +340,13 @@ class Angel extends Routable {
|
|||
/// provide a [type] argument as well.
|
||||
///
|
||||
/// If you are on `Dart >=2.0.0`, simply call `mountController<T>()`.
|
||||
Future<T> mountController<T extends Controller>([Type type]) {
|
||||
var controller = container.make<T>(type);
|
||||
Future<T> mountController<T extends Controller>([Type? type]) {
|
||||
T controller = container!.make<T>(type)!;
|
||||
return configure(controller.configureServer).then((_) => controller);
|
||||
}
|
||||
|
||||
/// Shorthand for calling `all('*', handler)`.
|
||||
Route<RequestHandler> fallback(RequestHandler handler) {
|
||||
Route<RequestHandler?> fallback(RequestHandler handler) {
|
||||
return all('*', handler);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,29 +68,31 @@ class Service<Id, Data> extends Routable {
|
|||
List<RequestHandler> get bootstrappers => [];
|
||||
|
||||
/// The [Angel] app powering this service.
|
||||
Angel app;
|
||||
Angel? app;
|
||||
|
||||
/// Closes this service, including any database connections or stream controllers.
|
||||
void close() {}
|
||||
|
||||
/// An optional [readData] function can be passed to handle non-map/non-json bodies.
|
||||
Service({FutureOr<Data> Function(RequestContext, ResponseContext) readData}) {
|
||||
_readData = readData ??
|
||||
(req, res) {
|
||||
if (req.bodyAsObject is! Data) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message:
|
||||
'Invalid request body. Expected $Data; found ${req.bodyAsObject} instead.');
|
||||
} else {
|
||||
return req.bodyAsObject as Data;
|
||||
}
|
||||
};
|
||||
Service(
|
||||
{FutureOr<Data> Function(RequestContext, ResponseContext?)? readData}) {
|
||||
_readData = readData;
|
||||
|
||||
_readData ??= (req, res) {
|
||||
if (req.bodyAsObject is! Data) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message:
|
||||
'Invalid request body. Expected $Data; found ${req.bodyAsObject} instead.');
|
||||
} else {
|
||||
return req.bodyAsObject as Data?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
FutureOr<Data> Function(RequestContext, ResponseContext) _readData;
|
||||
FutureOr<Data>? Function(RequestContext, ResponseContext?)? _readData;
|
||||
|
||||
/// A [Function] that reads the request body and converts it into [Data].
|
||||
FutureOr<Data> Function(RequestContext, ResponseContext) get readData =>
|
||||
FutureOr<Data>? Function(RequestContext, ResponseContext?)? get readData =>
|
||||
_readData;
|
||||
|
||||
/// Retrieves the first object from the result of calling [index] with the given [params].
|
||||
|
@ -103,7 +105,7 @@ class Service<Id, Data> extends Routable {
|
|||
///
|
||||
/// A custom [errorMessage] may be provided.
|
||||
Future<Data> findOne(
|
||||
[Map<String, dynamic> params,
|
||||
[Map<String, dynamic>? params,
|
||||
String errorMessage = 'No record was found matching the given query.']) {
|
||||
return index(params).then((result) {
|
||||
if (result == null) {
|
||||
|
@ -119,12 +121,12 @@ class Service<Id, Data> extends Routable {
|
|||
}
|
||||
|
||||
/// Retrieves all resources.
|
||||
Future<List<Data>> index([Map<String, dynamic> params]) {
|
||||
Future<List<Data>> index([Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
/// Retrieves the desired resource.
|
||||
Future<Data> read(Id id, [Map<String, dynamic> params]) {
|
||||
Future<Data> read(Id? id, [Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
|
@ -132,27 +134,27 @@ class Service<Id, Data> extends Routable {
|
|||
///
|
||||
/// Service implementations should override this to ensure data is fetched within a
|
||||
/// single round trip.
|
||||
Future<List<Data>> readMany(List<Id> ids, [Map<String, dynamic> params]) {
|
||||
Future<List<Data>> readMany(List<Id> ids, [Map<String, dynamic>? params]) {
|
||||
return Future.wait(ids.map((id) => read(id, params)));
|
||||
}
|
||||
|
||||
/// Creates a resource.
|
||||
Future<Data> create(Data data, [Map<String, dynamic> params]) {
|
||||
Future<Data> create(Data data, [Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
/// Modifies a resource.
|
||||
Future<Data> modify(Id id, Data data, [Map<String, dynamic> params]) {
|
||||
Future<Data> modify(Id? id, Data data, [Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
/// Overwrites a resource.
|
||||
Future<Data> update(Id id, Data data, [Map<String, dynamic> params]) {
|
||||
Future<Data> update(Id? id, Data data, [Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
/// Removes the given resource.
|
||||
Future<Data> remove(Id id, [Map<String, dynamic> params]) {
|
||||
Future<Data> remove(Id? id, [Map<String, dynamic>? params]) {
|
||||
throw AngelHttpException.methodNotAllowed();
|
||||
}
|
||||
|
||||
|
@ -161,14 +163,15 @@ class Service<Id, Data> extends Routable {
|
|||
///
|
||||
/// Handy utility for handling data in a type-safe manner.
|
||||
Service<Id, U> map<U>(U Function(Data) encoder, Data Function(U) decoder,
|
||||
{FutureOr<U> Function(RequestContext, ResponseContext) readData}) {
|
||||
{FutureOr<U> Function(RequestContext, ResponseContext)? readData}) {
|
||||
readData ??= (req, res) async {
|
||||
var inner = await this.readData(req, res);
|
||||
Data inner = await this.readData!(req, res)!;
|
||||
return encoder(inner);
|
||||
};
|
||||
|
||||
return AnonymousService<Id, U>(
|
||||
readData: readData,
|
||||
readData: readData as FutureOr<U> Function(
|
||||
RequestContext<dynamic>, ResponseContext<dynamic>?)?,
|
||||
index: ([params]) {
|
||||
return index(params).then((it) => it.map(encoder).toList());
|
||||
},
|
||||
|
@ -195,7 +198,7 @@ class Service<Id, Data> extends Routable {
|
|||
/// The single type argument, [T], is used to determine how to parse the [id].
|
||||
///
|
||||
/// For example, `parseId<bool>` attempts to parse the value as a [bool].
|
||||
static T parseId<T>(id) {
|
||||
static T? parseId<T>(id) {
|
||||
if (id == 'null' || id == null) {
|
||||
return null;
|
||||
} else if (T == String) {
|
||||
|
@ -214,7 +217,7 @@ class Service<Id, Data> extends Routable {
|
|||
}
|
||||
|
||||
/// Generates RESTful routes pointing to this class's methods.
|
||||
void addRoutes([Service service]) {
|
||||
void addRoutes([Service? service]) {
|
||||
_addRoutesInner(service ?? this, bootstrappers);
|
||||
}
|
||||
|
||||
|
@ -223,13 +226,13 @@ class Service<Id, Data> extends Routable {
|
|||
var handlers = List<RequestHandler>.from(handlerss);
|
||||
|
||||
// Add global middleware if declared on the instance itself
|
||||
Middleware before =
|
||||
getAnnotation<Middleware>(service, app.container.reflector);
|
||||
Middleware? before =
|
||||
getAnnotation<Middleware>(service, app!.container!.reflector);
|
||||
|
||||
if (before != null) handlers.addAll(before.handlers);
|
||||
|
||||
Middleware indexMiddleware =
|
||||
getAnnotation<Middleware>(service.index, app.container.reflector);
|
||||
Middleware? indexMiddleware =
|
||||
getAnnotation<Middleware>(service.index, app!.container!.reflector);
|
||||
get('/', (req, res) {
|
||||
return this.index(mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
|
@ -237,17 +240,19 @@ class Service<Id, Data> extends Routable {
|
|||
req.serviceParams
|
||||
]));
|
||||
},
|
||||
middleware: <RequestHandler>[]
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll((indexMiddleware == null) ? [] : indexMiddleware.handlers));
|
||||
..addAll((indexMiddleware == null)
|
||||
? []
|
||||
: indexMiddleware.handlers.toList()));
|
||||
|
||||
Middleware createMiddleware =
|
||||
getAnnotation<Middleware>(service.create, app.container.reflector);
|
||||
Middleware? createMiddleware =
|
||||
getAnnotation<Middleware>(service.create, app!.container!.reflector);
|
||||
post('/', (req, ResponseContext res) {
|
||||
return req.parseBody().then((_) async {
|
||||
return await this
|
||||
.create(
|
||||
await readData(req, res),
|
||||
(await readData!(req, res))!,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
|
@ -261,11 +266,12 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(createMiddleware == null) ? [] : createMiddleware.handlers));
|
||||
..addAll((createMiddleware == null)
|
||||
? []
|
||||
: createMiddleware.handlers.toList()));
|
||||
|
||||
Middleware readMiddleware =
|
||||
getAnnotation<Middleware>(service.read, app.container.reflector);
|
||||
Middleware? readMiddleware =
|
||||
getAnnotation<Middleware>(service.read, app!.container!.reflector);
|
||||
|
||||
get('/:id', (req, res) {
|
||||
return this.read(
|
||||
|
@ -278,15 +284,18 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
|
||||
..addAll((readMiddleware == null)
|
||||
? []
|
||||
: readMiddleware.handlers.toList()));
|
||||
|
||||
Middleware? modifyMiddleware =
|
||||
getAnnotation<Middleware>(service.modify, app!.container!.reflector);
|
||||
|
||||
Middleware modifyMiddleware =
|
||||
getAnnotation<Middleware>(service.modify, app.container.reflector);
|
||||
patch('/:id', (req, res) {
|
||||
return req.parseBody().then((_) async {
|
||||
return await this.modify(
|
||||
parseId<Id>(req.params['id']),
|
||||
await readData(req, res),
|
||||
(await readData!(req, res))!,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
|
@ -296,16 +305,17 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(modifyMiddleware == null) ? [] : modifyMiddleware.handlers));
|
||||
..addAll((modifyMiddleware == null)
|
||||
? []
|
||||
: modifyMiddleware.handlers.toList()));
|
||||
|
||||
Middleware updateMiddleware =
|
||||
getAnnotation<Middleware>(service.update, app.container.reflector);
|
||||
Middleware? updateMiddleware =
|
||||
getAnnotation<Middleware>(service.update, app!.container!.reflector);
|
||||
post('/:id', (req, res) {
|
||||
return req.parseBody().then((_) async {
|
||||
return await this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
await readData(req, res),
|
||||
(await readData!(req, res))!,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
|
@ -315,13 +325,15 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(updateMiddleware == null) ? [] : updateMiddleware.handlers));
|
||||
..addAll((updateMiddleware == null)
|
||||
? []
|
||||
: updateMiddleware.handlers.toList()));
|
||||
|
||||
put('/:id', (req, res) {
|
||||
return req.parseBody().then((_) async {
|
||||
return await this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
await readData(req, res),
|
||||
(await readData!(req, res))!,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
|
@ -331,11 +343,12 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(updateMiddleware == null) ? [] : updateMiddleware.handlers));
|
||||
..addAll((updateMiddleware == null)
|
||||
? []
|
||||
: updateMiddleware.handlers.toList()));
|
||||
|
||||
Middleware removeMiddleware =
|
||||
getAnnotation<Middleware>(service.remove, app.container.reflector);
|
||||
Middleware? removeMiddleware =
|
||||
getAnnotation<Middleware>(service.remove, app!.container!.reflector);
|
||||
delete('/', (req, res) {
|
||||
return this.remove(
|
||||
null,
|
||||
|
@ -347,8 +360,10 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(removeMiddleware == null) ? [] : removeMiddleware.handlers));
|
||||
..addAll((removeMiddleware == null)
|
||||
? []
|
||||
: removeMiddleware.handlers.toList()));
|
||||
|
||||
delete('/:id', (req, res) {
|
||||
return this.remove(
|
||||
parseId<Id>(req.params['id']),
|
||||
|
@ -360,8 +375,9 @@ class Service<Id, Data> extends Routable {
|
|||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(removeMiddleware == null) ? [] : removeMiddleware.handlers));
|
||||
..addAll((removeMiddleware == null)
|
||||
? []
|
||||
: removeMiddleware.handlers.toList()));
|
||||
|
||||
// REST compliance
|
||||
put('/', (req, res) => throw AngelHttpException.notFound());
|
||||
|
|
|
@ -21,13 +21,13 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
|
|||
@override
|
||||
Uri get uri => server == null
|
||||
? Uri()
|
||||
: Uri(scheme: 'http', host: server.address.address, port: server.port);
|
||||
: Uri(scheme: 'http', host: server!.address.address, port: server!.port);
|
||||
|
||||
AngelHttp._(Angel app,
|
||||
AngelHttp._(Angel? app,
|
||||
Future<HttpServer> Function(dynamic, int) serverGenerator, bool useZone)
|
||||
: super(app, serverGenerator, useZone: useZone);
|
||||
|
||||
factory AngelHttp(Angel app, {bool useZone = true}) {
|
||||
factory AngelHttp(Angel? app, {bool useZone = true}) {
|
||||
return AngelHttp._(app, HttpServer.bind, useZone);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
|
|||
/// the server.
|
||||
factory AngelHttp.secure(
|
||||
Angel app, String certificateChainPath, String serverKeyPath,
|
||||
{String password, bool useZone = true}) {
|
||||
{String? password, bool useZone = true}) {
|
||||
var certificateChain =
|
||||
Platform.script.resolve(certificateChainPath).toFilePath();
|
||||
var serverKey = Platform.script.resolve(serverKeyPath).toFilePath();
|
||||
|
@ -64,7 +64,7 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
|
|||
|
||||
/// Use [server] instead.
|
||||
@deprecated
|
||||
HttpServer get httpServer => server;
|
||||
HttpServer? get httpServer => server;
|
||||
|
||||
Future handleRequest(HttpRequest request) =>
|
||||
handleRawRequest(request, request.response);
|
||||
|
@ -87,17 +87,27 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
|
|||
HttpRequest request, HttpResponse response) {
|
||||
var path = request.uri.path.replaceAll(_straySlashes, '');
|
||||
if (path.isEmpty) path = '/';
|
||||
return HttpRequestContext.from(request, app, path);
|
||||
return HttpRequestContext.from(request, app!, path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<HttpResponseContext> createResponseContext(
|
||||
HttpRequest request, HttpResponse response,
|
||||
[HttpRequestContext correspondingRequest]) {
|
||||
return Future<HttpResponseContext>.value(
|
||||
HttpResponseContext(response, app, correspondingRequest)
|
||||
..serializer = (app.serializer ?? json.encode)
|
||||
..encoders.addAll(app.encoders ?? {}));
|
||||
[HttpRequestContext? correspondingRequest]) {
|
||||
// TODO: Refactored to overcome NNBD migration error
|
||||
HttpResponseContext context =
|
||||
HttpResponseContext(response, app, correspondingRequest);
|
||||
|
||||
if (app!.serializer == null) {
|
||||
context.serializer = json.encode;
|
||||
} else {
|
||||
context.serializer = app!.serializer;
|
||||
}
|
||||
return Future<HttpResponseContext>.value(context);
|
||||
// return Future<HttpResponseContext>.value(
|
||||
// HttpResponseContext(response, app, correspondingRequest)
|
||||
// ..serializer = (app.serializer ?? json.encode)
|
||||
// ..encoders.addAll(app.encoders ?? {}));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -114,8 +124,8 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
|
|||
response.headers.contentLength = length;
|
||||
|
||||
@override
|
||||
void setHeader(HttpResponse response, String key, String value) =>
|
||||
response.headers.set(key, value);
|
||||
void setHeader(HttpResponse response, String key, String? value) =>
|
||||
response.headers.set(key, value!);
|
||||
|
||||
@override
|
||||
void setStatusCode(HttpResponse response, int value) =>
|
||||
|
|
|
@ -7,40 +7,40 @@ import 'package:http_parser/http_parser.dart';
|
|||
import '../core/core.dart';
|
||||
|
||||
/// An implementation of [RequestContext] that wraps a [HttpRequest].
|
||||
class HttpRequestContext extends RequestContext<HttpRequest> {
|
||||
Container _container;
|
||||
MediaType _contentType;
|
||||
HttpRequest _io;
|
||||
String _override, _path;
|
||||
class HttpRequestContext extends RequestContext<HttpRequest?> {
|
||||
Container? _container;
|
||||
MediaType? _contentType;
|
||||
HttpRequest? _io;
|
||||
String? _override, _path;
|
||||
|
||||
@override
|
||||
Container get container => _container;
|
||||
Container? get container => _container;
|
||||
|
||||
@override
|
||||
MediaType get contentType {
|
||||
MediaType? get contentType {
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Cookie> get cookies {
|
||||
return rawRequest.cookies;
|
||||
return rawRequest!.cookies;
|
||||
}
|
||||
|
||||
@override
|
||||
HttpHeaders get headers {
|
||||
return rawRequest.headers;
|
||||
return rawRequest!.headers;
|
||||
}
|
||||
|
||||
@override
|
||||
String get hostname {
|
||||
return rawRequest.headers.value('host');
|
||||
String? get hostname {
|
||||
return rawRequest!.headers.value('host');
|
||||
}
|
||||
|
||||
/// The underlying [HttpRequest] instance underneath this context.
|
||||
HttpRequest get rawRequest => _io;
|
||||
HttpRequest? get rawRequest => _io;
|
||||
|
||||
@override
|
||||
Stream<List<int>> get body => _io;
|
||||
Stream<List<int>>? get body => _io;
|
||||
|
||||
@override
|
||||
String get method {
|
||||
|
@ -49,34 +49,34 @@ class HttpRequestContext extends RequestContext<HttpRequest> {
|
|||
|
||||
@override
|
||||
String get originalMethod {
|
||||
return rawRequest.method;
|
||||
return rawRequest!.method;
|
||||
}
|
||||
|
||||
@override
|
||||
String get path {
|
||||
String? get path {
|
||||
return _path;
|
||||
}
|
||||
|
||||
@override
|
||||
InternetAddress get remoteAddress {
|
||||
return rawRequest.connectionInfo.remoteAddress;
|
||||
return rawRequest!.connectionInfo!.remoteAddress;
|
||||
}
|
||||
|
||||
@override
|
||||
HttpSession get session {
|
||||
return rawRequest.session;
|
||||
return rawRequest!.session;
|
||||
}
|
||||
|
||||
@override
|
||||
Uri get uri {
|
||||
return rawRequest.uri;
|
||||
return rawRequest!.uri;
|
||||
}
|
||||
|
||||
/// Magically transforms an [HttpRequest] into a [RequestContext].
|
||||
static Future<HttpRequestContext> from(
|
||||
HttpRequest request, Angel app, String path) {
|
||||
HttpRequestContext ctx = HttpRequestContext()
|
||||
.._container = app.container.createChild();
|
||||
.._container = app.container!.createChild();
|
||||
|
||||
String override = request.method;
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
/// The underlying [HttpResponse] under this instance.
|
||||
@override
|
||||
final HttpResponse rawResponse;
|
||||
Angel app;
|
||||
Angel? app;
|
||||
|
||||
LockableBytesBuilder _buffer;
|
||||
LockableBytesBuilder? _buffer;
|
||||
|
||||
final HttpRequestContext _correspondingRequest;
|
||||
final HttpRequestContext? _correspondingRequest;
|
||||
bool _isDetached = false, _isClosed = false, _streamInitialized = false;
|
||||
|
||||
HttpResponseContext(this.rawResponse, this.app, [this._correspondingRequest]);
|
||||
|
@ -28,7 +28,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
}
|
||||
|
||||
@override
|
||||
RequestContext get correspondingRequest {
|
||||
RequestContext? get correspondingRequest {
|
||||
return _correspondingRequest;
|
||||
}
|
||||
|
||||
|
@ -41,10 +41,10 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
bool get isBuffered => _buffer != null;
|
||||
|
||||
@override
|
||||
BytesBuilder get buffer => _buffer;
|
||||
BytesBuilder? get buffer => _buffer;
|
||||
|
||||
@override
|
||||
void addError(Object error, [StackTrace stackTrace]) {
|
||||
void addError(Object error, [StackTrace? stackTrace]) {
|
||||
rawResponse.addError(error, stackTrace);
|
||||
super.addError(error, stackTrace);
|
||||
}
|
||||
|
@ -54,10 +54,10 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
_buffer = LockableBytesBuilder();
|
||||
}
|
||||
|
||||
Iterable<String> __allowedEncodings;
|
||||
Iterable<String>? __allowedEncodings;
|
||||
|
||||
Iterable<String> get _allowedEncodings {
|
||||
return __allowedEncodings ??= correspondingRequest.headers
|
||||
Iterable<String>? get _allowedEncodings {
|
||||
return __allowedEncodings ??= correspondingRequest!.headers!
|
||||
.value('accept-encoding')
|
||||
?.split(',')
|
||||
?.map((s) => s.trim())
|
||||
|
@ -89,7 +89,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
headers.forEach(rawResponse.headers.set);
|
||||
|
||||
if (headers.containsKey('content-length')) {
|
||||
rawResponse.contentLength = int.tryParse(headers['content-length']) ??
|
||||
rawResponse.contentLength = int.tryParse(headers['content-length']!) ??
|
||||
rawResponse.contentLength;
|
||||
}
|
||||
|
||||
|
@ -100,8 +100,8 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -134,8 +134,8 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -145,7 +145,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
}
|
||||
|
||||
if (encoder != null) {
|
||||
output = encoders[key].bind(output);
|
||||
output = encoders[key]!.bind(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -165,8 +165,8 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -176,7 +176,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
}
|
||||
|
||||
if (encoder != null) {
|
||||
data = encoders[key].convert(data);
|
||||
data = encoders[key]!.convert(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
rawResponse.add(data);
|
||||
}
|
||||
} else {
|
||||
buffer.add(data);
|
||||
buffer!.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
|
|||
// this try/catch prevents a crash.
|
||||
}
|
||||
} else {
|
||||
_buffer.lock();
|
||||
_buffer!.lock();
|
||||
}
|
||||
|
||||
_isClosed = true;
|
||||
|
|
|
@ -18,14 +18,14 @@ Future<SecureServerSocket> startSharedHttp2(
|
|||
/// Adapts `package:http2`'s [ServerTransportConnection] to serve Angel.
|
||||
class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
||||
SecureServerSocket, Http2RequestContext, Http2ResponseContext> {
|
||||
final ServerSettings settings;
|
||||
AngelHttp _http;
|
||||
final ServerSettings? settings;
|
||||
late AngelHttp _http;
|
||||
final StreamController<HttpRequest> _onHttp1 = StreamController();
|
||||
final Map<String, MockHttpSession> _sessions = {};
|
||||
final Uuid _uuid = Uuid();
|
||||
_AngelHttp2ServerSocket _artificial;
|
||||
_AngelHttp2ServerSocket? _artificial;
|
||||
|
||||
SecureServerSocket get socket => _artificial;
|
||||
SecureServerSocket? get socket => _artificial;
|
||||
|
||||
AngelHttp2._(
|
||||
Angel app,
|
||||
|
@ -45,7 +45,7 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
}
|
||||
|
||||
factory AngelHttp2(Angel app, SecurityContext securityContext,
|
||||
{bool useZone = true, bool allowHttp1 = false, ServerSettings settings}) {
|
||||
{bool useZone = true, bool allowHttp1 = false, ServerSettings? settings}) {
|
||||
return AngelHttp2.custom(app, securityContext, SecureServerSocket.bind,
|
||||
allowHttp1: allowHttp1, settings: settings);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
address, int port, SecurityContext ctx),
|
||||
{bool useZone = true,
|
||||
bool allowHttp1 = false,
|
||||
ServerSettings settings}) {
|
||||
ServerSettings? settings}) {
|
||||
return AngelHttp2._(app, (address, port) {
|
||||
var addr = address is InternetAddress
|
||||
? address
|
||||
|
@ -70,14 +70,14 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
Stream<HttpRequest> get onHttp1 => _onHttp1.stream;
|
||||
|
||||
@override
|
||||
Future<SecureServerSocket> generateServer([address, int port]) async {
|
||||
var s = await serverGenerator(address ?? '127.0.0.1', port ?? 0);
|
||||
Future<SecureServerSocket> generateServer([address, int? port]) async {
|
||||
SecureServerSocket s = await serverGenerator(address ?? '127.0.0.1', port ?? 0);
|
||||
return _artificial = _AngelHttp2ServerSocket(s, this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SecureServerSocket> close() async {
|
||||
await _artificial.close();
|
||||
await _artificial!.close();
|
||||
await _http?.close();
|
||||
return await super.close();
|
||||
}
|
||||
|
@ -98,15 +98,15 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
@override
|
||||
Future<Http2RequestContext> createRequestContext(
|
||||
Socket request, ServerTransportStream response) {
|
||||
return Http2RequestContext.from(response, request, app, _sessions, _uuid);
|
||||
return Http2RequestContext.from(response, request, app!, _sessions, _uuid);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Http2ResponseContext> createResponseContext(
|
||||
Socket request, ServerTransportStream response,
|
||||
[Http2RequestContext correspondingRequest]) async {
|
||||
[Http2RequestContext? correspondingRequest]) async {
|
||||
return Http2ResponseContext(app, response, correspondingRequest)
|
||||
..encoders.addAll(app.encoders);
|
||||
..encoders.addAll(app!.encoders);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -128,8 +128,8 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
}
|
||||
|
||||
@override
|
||||
void setHeader(ServerTransportStream response, String key, String value) {
|
||||
response.sendHeaders([Header.ascii(key, value)]);
|
||||
void setHeader(ServerTransportStream response, String key, String? value) {
|
||||
response.sendHeaders([Header.ascii(key, value!)]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -140,8 +140,8 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
|
|||
@override
|
||||
Uri get uri => Uri(
|
||||
scheme: 'https',
|
||||
host: server.address.address,
|
||||
port: server.port != 443 ? server.port : null);
|
||||
host: server!.address.address,
|
||||
port: server!.port != 443 ? server!.port : null);
|
||||
|
||||
@override
|
||||
void writeStringToResponse(ServerTransportStream response, String value) {
|
||||
|
@ -173,8 +173,8 @@ class _FakeServerSocket extends Stream<Socket> implements ServerSocket {
|
|||
int get port => angel.port;
|
||||
|
||||
@override
|
||||
StreamSubscription<Socket> listen(void Function(Socket event) onData,
|
||||
{Function onError, void Function() onDone, bool cancelOnError}) {
|
||||
StreamSubscription<Socket> listen(void Function(Socket event)? onData,
|
||||
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
|
||||
return _ctrl.stream.listen(onData,
|
||||
cancelOnError: cancelOnError, onError: onError, onDone: onDone);
|
||||
}
|
||||
|
@ -185,8 +185,8 @@ class _AngelHttp2ServerSocket extends Stream<SecureSocket>
|
|||
final SecureServerSocket socket;
|
||||
final AngelHttp2 driver;
|
||||
final _ctrl = StreamController<SecureSocket>();
|
||||
_FakeServerSocket _fake;
|
||||
StreamSubscription _sub;
|
||||
late _FakeServerSocket _fake;
|
||||
StreamSubscription? _sub;
|
||||
|
||||
_AngelHttp2ServerSocket(this.socket, this.driver) {
|
||||
_fake = _FakeServerSocket(this);
|
||||
|
@ -208,7 +208,7 @@ class _AngelHttp2ServerSocket extends Stream<SecureSocket>
|
|||
},
|
||||
onDone: _ctrl.close,
|
||||
onError: (e, st) {
|
||||
driver.app.logger.warning(
|
||||
driver.app!.logger!.warning(
|
||||
'HTTP/2 incoming connection failure: ', e, st as StackTrace);
|
||||
},
|
||||
);
|
||||
|
@ -227,10 +227,10 @@ class _AngelHttp2ServerSocket extends Stream<SecureSocket>
|
|||
|
||||
@override
|
||||
StreamSubscription<SecureSocket> listen(
|
||||
void Function(SecureSocket event) onData,
|
||||
{Function onError,
|
||||
void Function() onDone,
|
||||
bool cancelOnError}) {
|
||||
void Function(SecureSocket event)? onData,
|
||||
{Function? onError,
|
||||
void Function()? onDone,
|
||||
bool? cancelOnError}) {
|
||||
return _ctrl.stream.listen(onData,
|
||||
cancelOnError: cancelOnError, onError: onError, onDone: onDone);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:angel_container/src/container.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:mock_request/mock_request.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
@ -10,16 +11,16 @@ import 'package:uuid/uuid.dart';
|
|||
final RegExp _comma = RegExp(r',\s*');
|
||||
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
||||
|
||||
class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
||||
class Http2RequestContext extends RequestContext<ServerTransportStream?> {
|
||||
final StreamController<List<int>> _body = StreamController();
|
||||
final Container container;
|
||||
List<Cookie> _cookies;
|
||||
HttpHeaders _headers;
|
||||
String _method, _override, _path;
|
||||
HttpSession _session;
|
||||
Socket _socket;
|
||||
ServerTransportStream _stream;
|
||||
Uri _uri;
|
||||
List<Cookie>? _cookies;
|
||||
HttpHeaders? _headers;
|
||||
String? _method, _override, _path;
|
||||
HttpSession? _session;
|
||||
late Socket _socket;
|
||||
ServerTransportStream? _stream;
|
||||
Uri? _uri;
|
||||
|
||||
Http2RequestContext._(this.container);
|
||||
|
||||
|
@ -33,7 +34,7 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
Map<String, MockHttpSession> sessions,
|
||||
Uuid uuid) {
|
||||
var c = Completer<Http2RequestContext>();
|
||||
var req = Http2RequestContext._(app.container.createChild())
|
||||
var req = Http2RequestContext._(app.container!.createChild())
|
||||
..app = app
|
||||
.._socket = socket
|
||||
.._stream = stream;
|
||||
|
@ -124,7 +125,7 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
|
||||
// Apply session
|
||||
var dartSessId =
|
||||
cookies.firstWhere((c) => c.name == 'DARTSESSID', orElse: () => null);
|
||||
cookies.firstWhereOrNull((c) => c.name == 'DARTSESSID');
|
||||
|
||||
if (dartSessId == null) {
|
||||
dartSessId = Cookie('DARTSESSID', uuid.v4());
|
||||
|
@ -132,23 +133,23 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
|
||||
req._session = sessions.putIfAbsent(
|
||||
dartSessId.value,
|
||||
() => MockHttpSession(id: dartSessId.value),
|
||||
() => MockHttpSession(id: dartSessId!.value),
|
||||
);
|
||||
|
||||
return c.future;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Cookie> get cookies => _cookies;
|
||||
List<Cookie>? get cookies => _cookies;
|
||||
|
||||
/// The underlying HTTP/2 [ServerTransportStream].
|
||||
ServerTransportStream get stream => _stream;
|
||||
ServerTransportStream? get stream => _stream;
|
||||
|
||||
@override
|
||||
Uri get uri => _uri;
|
||||
Uri? get uri => _uri;
|
||||
|
||||
@override
|
||||
HttpSession get session {
|
||||
HttpSession? get session {
|
||||
return _session;
|
||||
}
|
||||
|
||||
|
@ -156,25 +157,25 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
InternetAddress get remoteAddress => _socket.remoteAddress;
|
||||
|
||||
@override
|
||||
String get path {
|
||||
String? get path {
|
||||
return _path;
|
||||
}
|
||||
|
||||
@override
|
||||
String get originalMethod {
|
||||
String? get originalMethod {
|
||||
return _method;
|
||||
}
|
||||
|
||||
@override
|
||||
String get method {
|
||||
String? get method {
|
||||
return _override ?? _method;
|
||||
}
|
||||
|
||||
@override
|
||||
String get hostname => _headers.value('host');
|
||||
String? get hostname => _headers!.value('host');
|
||||
|
||||
@override
|
||||
HttpHeaders get headers => _headers;
|
||||
HttpHeaders? get headers => _headers;
|
||||
|
||||
@override
|
||||
Future close() {
|
||||
|
@ -183,5 +184,5 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
}
|
||||
|
||||
@override
|
||||
ServerTransportStream get rawRequest => _stream;
|
||||
ServerTransportStream? get rawRequest => _stream;
|
||||
}
|
||||
|
|
|
@ -6,24 +6,24 @@ import 'package:http2/transport.dart';
|
|||
import 'http2_request_context.dart';
|
||||
|
||||
class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
||||
final Angel app;
|
||||
final Angel? app;
|
||||
final ServerTransportStream stream;
|
||||
|
||||
ServerTransportStream get rawResponse => stream;
|
||||
|
||||
LockableBytesBuilder _buffer;
|
||||
LockableBytesBuilder? _buffer;
|
||||
|
||||
final Http2RequestContext _req;
|
||||
final Http2RequestContext? _req;
|
||||
|
||||
bool _isDetached = false,
|
||||
_isClosed = false,
|
||||
_streamInitialized = false,
|
||||
_isPush = false;
|
||||
|
||||
Uri _targetUri;
|
||||
Uri? _targetUri;
|
||||
|
||||
Http2ResponseContext(this.app, this.stream, this._req) {
|
||||
_targetUri = _req.uri;
|
||||
_targetUri = _req!.uri;
|
||||
}
|
||||
|
||||
final List<Http2ResponseContext> _pushes = [];
|
||||
|
@ -43,9 +43,9 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
}
|
||||
|
||||
@override
|
||||
RequestContext get correspondingRequest => _req;
|
||||
RequestContext? get correspondingRequest => _req;
|
||||
|
||||
Uri get targetUri => _targetUri;
|
||||
Uri? get targetUri => _targetUri;
|
||||
|
||||
@override
|
||||
bool get isOpen {
|
||||
|
@ -56,10 +56,10 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
bool get isBuffered => _buffer != null;
|
||||
|
||||
@override
|
||||
BytesBuilder get buffer => _buffer;
|
||||
BytesBuilder? get buffer => _buffer;
|
||||
|
||||
@override
|
||||
void addError(Object error, [StackTrace stackTrace]) {
|
||||
void addError(Object error, [StackTrace? stackTrace]) {
|
||||
super.addError(error, stackTrace);
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,8 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -98,11 +98,11 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
|
||||
// Add all normal headers
|
||||
for (var key in this.headers.keys) {
|
||||
headers.add(Header.ascii(key.toLowerCase(), this.headers[key]));
|
||||
headers.add(Header.ascii(key.toLowerCase(), this.headers[key]!));
|
||||
}
|
||||
|
||||
// Persist session ID
|
||||
cookies.add(Cookie('DARTSESSID', _req.session.id));
|
||||
cookies.add(Cookie('DARTSESSID', _req!.session!.id));
|
||||
|
||||
// Send all cookies
|
||||
for (var cookie in cookies) {
|
||||
|
@ -113,10 +113,10 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
return _streamInitialized = true;
|
||||
}
|
||||
|
||||
Iterable<String> __allowedEncodings;
|
||||
Iterable<String>? __allowedEncodings;
|
||||
|
||||
Iterable<String> get _allowedEncodings {
|
||||
return __allowedEncodings ??= correspondingRequest.headers
|
||||
Iterable<String>? get _allowedEncodings {
|
||||
return __allowedEncodings ??= correspondingRequest!.headers!
|
||||
.value('accept-encoding')
|
||||
?.split(',')
|
||||
?.map((s) => s.trim())
|
||||
|
@ -138,8 +138,8 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -149,7 +149,7 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
}
|
||||
|
||||
if (encoder != null) {
|
||||
output = encoders[key].bind(output);
|
||||
output = encoders[key]!.bind(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +169,8 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
if (!_isClosed) {
|
||||
if (encoders.isNotEmpty && correspondingRequest != null) {
|
||||
if (_allowedEncodings != null) {
|
||||
for (var encodingName in _allowedEncodings) {
|
||||
Converter<List<int>, List<int>> encoder;
|
||||
for (var encodingName in _allowedEncodings!) {
|
||||
Converter<List<int>, List<int>>? encoder;
|
||||
String key = encodingName;
|
||||
|
||||
if (encoders.containsKey(encodingName)) {
|
||||
|
@ -180,7 +180,7 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
}
|
||||
|
||||
if (encoder != null) {
|
||||
data = encoders[key].convert(data);
|
||||
data = encoders[key]!.convert(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
stream.sendData(data);
|
||||
}
|
||||
} else {
|
||||
buffer.add(data);
|
||||
buffer!.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
/// Pushes a resource to the client.
|
||||
Http2ResponseContext push(String path,
|
||||
{Map<String, String> headers = const {}, String method = 'GET'}) {
|
||||
var targetUri = _req.uri.replace(path: path);
|
||||
var targetUri = _req!.uri!.replace(path: path);
|
||||
|
||||
var h = <Header>[
|
||||
Header.ascii(':authority', targetUri.authority),
|
||||
|
@ -218,7 +218,7 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
|
|||
];
|
||||
|
||||
for (var key in headers.keys) {
|
||||
h.add(Header.ascii(key, headers[key]));
|
||||
h.add(Header.ascii(key, headers[key]!));
|
||||
}
|
||||
|
||||
var s = stream.push(h);
|
||||
|
|
|
@ -12,7 +12,7 @@ abstract class SafeCtrl<T> {
|
|||
|
||||
void add(T event);
|
||||
|
||||
void addError(error, [StackTrace stackTrace]);
|
||||
void addError(error, [StackTrace? stackTrace]);
|
||||
|
||||
Future close();
|
||||
|
||||
|
@ -20,16 +20,16 @@ abstract class SafeCtrl<T> {
|
|||
}
|
||||
|
||||
class _SingleSafeCtrl<T> implements SafeCtrl<T> {
|
||||
StreamController<T> _stream;
|
||||
late StreamController<T> _stream;
|
||||
bool _hasListener = false, _initialized = false;
|
||||
_InitCallback _initializer;
|
||||
_InitCallback? _initializer;
|
||||
|
||||
_SingleSafeCtrl() {
|
||||
_stream = StreamController<T>(onListen: () {
|
||||
_hasListener = true;
|
||||
|
||||
if (!_initialized && _initializer != null) {
|
||||
_initializer();
|
||||
_initializer!();
|
||||
_initialized = true;
|
||||
}
|
||||
}, onPause: () {
|
||||
|
@ -50,8 +50,8 @@ class _SingleSafeCtrl<T> implements SafeCtrl<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
void addError(error, [StackTrace stackTrace]) {
|
||||
if (_hasListener) _stream.addError(error, stackTrace);
|
||||
void addError(error, [StackTrace? stackTrace]) {
|
||||
if (_hasListener) _stream.addError(error as Object, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -65,7 +65,7 @@ class _SingleSafeCtrl<T> implements SafeCtrl<T> {
|
|||
if (!_hasListener) {
|
||||
_initializer = callback;
|
||||
} else {
|
||||
_initializer();
|
||||
_initializer!();
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
@ -73,17 +73,17 @@ class _SingleSafeCtrl<T> implements SafeCtrl<T> {
|
|||
}
|
||||
|
||||
class _BroadcastSafeCtrl<T> implements SafeCtrl<T> {
|
||||
StreamController<T> _stream;
|
||||
late StreamController<T> _stream;
|
||||
int _listeners = 0;
|
||||
bool _initialized = false;
|
||||
_InitCallback _initializer;
|
||||
_InitCallback? _initializer;
|
||||
|
||||
_BroadcastSafeCtrl() {
|
||||
_stream = StreamController<T>.broadcast(onListen: () {
|
||||
_listeners++;
|
||||
|
||||
if (!_initialized && _initializer != null) {
|
||||
_initializer();
|
||||
_initializer!();
|
||||
_initialized = true;
|
||||
}
|
||||
}, onCancel: () {
|
||||
|
@ -100,8 +100,8 @@ class _BroadcastSafeCtrl<T> implements SafeCtrl<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
void addError(error, [StackTrace stackTrace]) {
|
||||
if (_listeners > 0) _stream.addError(error, stackTrace);
|
||||
void addError(error, [StackTrace? stackTrace]) {
|
||||
if (_listeners > 0) _stream.addError(error as Object, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -115,7 +115,7 @@ class _BroadcastSafeCtrl<T> implements SafeCtrl<T> {
|
|||
if (_listeners <= 0) {
|
||||
_initializer = callback;
|
||||
} else {
|
||||
_initializer();
|
||||
_initializer!();
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,25 +2,25 @@ import 'package:angel_container/angel_container.dart';
|
|||
|
||||
final RegExp straySlashes = RegExp(r'(^/+)|(/+$)');
|
||||
|
||||
T matchingAnnotation<T>(List<ReflectedInstance> metadata) {
|
||||
T? matchingAnnotation<T>(List<ReflectedInstance> metadata) {
|
||||
for (ReflectedInstance metaDatum in metadata) {
|
||||
if (metaDatum.type.reflectedType == T) {
|
||||
return metaDatum.reflectee as T;
|
||||
return metaDatum.reflectee as T?;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
T getAnnotation<T>(obj, Reflector reflector) {
|
||||
T? getAnnotation<T>(obj, Reflector? reflector) {
|
||||
if (reflector == null) {
|
||||
return null;
|
||||
} else {
|
||||
if (obj is Function) {
|
||||
var methodMirror = reflector.reflectFunction(obj);
|
||||
var methodMirror = reflector.reflectFunction(obj)!;
|
||||
return matchingAnnotation<T>(methodMirror.annotations);
|
||||
} else {
|
||||
var classMirror = reflector.reflectClass(obj.runtimeType as Type);
|
||||
var classMirror = reflector.reflectClass(obj.runtimeType as Type)!;
|
||||
return matchingAnnotation<T>(classMirror.annotations);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ author: Tobe O <thosakwe@gmail.com>
|
|||
homepage: https://github.com/angel-dart/angel_framework
|
||||
publish_to: none
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel_container:
|
||||
git:
|
||||
|
@ -60,6 +60,7 @@ dependencies:
|
|||
string_scanner: ^1.1.0
|
||||
tuple: ^2.0.0
|
||||
uuid: ^3.0.1
|
||||
collection: ^1.15.0
|
||||
dev_dependencies:
|
||||
http: ^0.13.1
|
||||
io: ^1.0.0
|
||||
|
|
|
@ -8,9 +8,9 @@ import 'package:test/test.dart';
|
|||
import 'pretty_log.dart';
|
||||
|
||||
void main() {
|
||||
http.IOClient client;
|
||||
AngelHttp driver;
|
||||
Logger logger;
|
||||
late http.IOClient client;
|
||||
late AngelHttp driver;
|
||||
late Logger logger;
|
||||
|
||||
setUp(() async {
|
||||
client = http.IOClient();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:angel_container/mirrors.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
|
@ -44,7 +45,7 @@ main() {
|
|||
});
|
||||
|
||||
group('disallow null', () {
|
||||
RequestContext req;
|
||||
late RequestContext req;
|
||||
|
||||
setUp(() async {
|
||||
req = await acceptContentTypes();
|
||||
|
@ -58,8 +59,9 @@ main() {
|
|||
|
||||
Future<RequestContext> acceptContentTypes(
|
||||
[Iterable<String> contentTypes = const []]) {
|
||||
var headerString = contentTypes.isEmpty ? null : contentTypes.join(',');
|
||||
var rq = MockHttpRequest('GET', ENDPOINT);
|
||||
var headerString =
|
||||
contentTypes.isEmpty ? ContentType.html : contentTypes.join(',');
|
||||
var rq = MockHttpRequest('GET', ENDPOINT, persistentConnection: false);
|
||||
rq.headers.set('accept', headerString);
|
||||
rq.close();
|
||||
var app = Angel(reflector: MirrorsReflector());
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:test/test.dart';
|
|||
|
||||
main() {
|
||||
test('custom methods', () async {
|
||||
var svc = AnonymousService<String, String>(
|
||||
var svc = AnonymousService<String?, String?>(
|
||||
index: ([p]) async => ['index'],
|
||||
read: (id, [p]) async => 'read',
|
||||
create: (data, [p]) async => 'create',
|
||||
|
|
|
@ -13,9 +13,10 @@ void main() {
|
|||
Future<RequestContext> request(
|
||||
{bool asJson = true,
|
||||
bool parse = true,
|
||||
Map<String, dynamic> bodyFields,
|
||||
List bodyList}) async {
|
||||
var rq = MockHttpRequest('POST', Uri(path: '/'));
|
||||
Map<String, dynamic>? bodyFields,
|
||||
List? bodyList}) async {
|
||||
var rq =
|
||||
MockHttpRequest('POST', Uri(path: '/'), persistentConnection: false);
|
||||
|
||||
if (bodyFields != null) {
|
||||
if (asJson) {
|
||||
|
@ -115,16 +116,16 @@ void main() {
|
|||
}
|
||||
|
||||
class Todo {
|
||||
String text;
|
||||
bool completed;
|
||||
String? text;
|
||||
bool? completed;
|
||||
|
||||
Todo({this.text, this.completed});
|
||||
|
||||
static Todo fromMap(Map m) =>
|
||||
Todo(text: m['text'] as String, completed: m['complete'] as bool);
|
||||
static Todo fromMap(Map? m) =>
|
||||
Todo(text: m!['text'] as String?, completed: m['complete'] as bool?);
|
||||
}
|
||||
|
||||
class TodoCodec extends Codec<Todo, Map> {
|
||||
class TodoCodec extends Codec<Todo, Map?> {
|
||||
@override
|
||||
Converter<Map, Todo> get decoder => TodoDecoder();
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import 'package:angel_framework/angel_framework.dart';
|
|||
import 'package:matcher/matcher.dart';
|
||||
|
||||
class Todo extends Model {
|
||||
String text;
|
||||
String over;
|
||||
String? text;
|
||||
String? over;
|
||||
|
||||
Todo({this.text, this.over});
|
||||
|
||||
|
|
|
@ -70,39 +70,39 @@ bool bar(RequestContext req, ResponseContext res) {
|
|||
}
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
TodoController todoController;
|
||||
NoExposeController noExposeCtrl;
|
||||
HttpServer server;
|
||||
Angel? app;
|
||||
late TodoController todoController;
|
||||
late NoExposeController noExposeCtrl;
|
||||
late HttpServer server;
|
||||
http.Client client = http.Client();
|
||||
String url;
|
||||
String? url;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
app.get(
|
||||
app!.get(
|
||||
"/redirect",
|
||||
(req, res) async =>
|
||||
res.redirectToAction("TodoController@foo", {"foo": "world"}));
|
||||
|
||||
// Register as a singleton, just for the purpose of this test
|
||||
if (!app.container.has<TodoController>()) {
|
||||
app.container.registerSingleton(todoController = TodoController());
|
||||
if (!app!.container!.has<TodoController>()) {
|
||||
app!.container!.registerSingleton(todoController = TodoController());
|
||||
}
|
||||
|
||||
// Using mountController<T>();
|
||||
await app.mountController<TodoController>();
|
||||
await app!.mountController<TodoController>();
|
||||
|
||||
noExposeCtrl = await app.mountController<NoExposeController>();
|
||||
noExposeCtrl = await app!.mountController<NoExposeController>();
|
||||
|
||||
// Place controller in group. The applyRoutes() call, however, is async.
|
||||
// Until https://github.com/angel-dart/route/issues/28 is closed,
|
||||
// this will need to be done by manually mounting the router.
|
||||
var subRouter = Router<RequestHandler>();
|
||||
await todoController.applyRoutes(subRouter, app.container.reflector);
|
||||
app.mount('/ctrl_group', subRouter);
|
||||
await todoController.applyRoutes(subRouter, app!.container!.reflector);
|
||||
app!.mount('/ctrl_group', subRouter);
|
||||
|
||||
print(app.controllers);
|
||||
app.dumpTree();
|
||||
print(app!.controllers);
|
||||
app!.dumpTree();
|
||||
|
||||
server = await AngelHttp(app).startServer();
|
||||
url = 'http://${server.address.address}:${server.port}';
|
||||
|
@ -122,7 +122,7 @@ main() {
|
|||
var app = Angel(reflector: MirrorsReflector());
|
||||
app.get(
|
||||
'/foo',
|
||||
ioc(({String bar}) {
|
||||
ioc(({String? bar}) {
|
||||
return 2;
|
||||
}, optional: ['bar']));
|
||||
var rq = MockHttpRequest('GET', Uri(path: 'foo'));
|
||||
|
@ -171,7 +171,7 @@ main() {
|
|||
|
||||
group('optional expose', () {
|
||||
test('removes suffixes from controller names', () {
|
||||
expect(noExposeCtrl.mountPoint.path, 'no_expose');
|
||||
expect(noExposeCtrl.mountPoint!.path, 'no_expose');
|
||||
});
|
||||
|
||||
test('mounts correct routes', () {
|
||||
|
@ -182,7 +182,7 @@ main() {
|
|||
|
||||
test('mounts correct methods', () {
|
||||
void expectMethod(String name, String method) {
|
||||
expect(noExposeCtrl.routeMappings[name].method, method);
|
||||
expect(noExposeCtrl.routeMappings[name]!.method, method);
|
||||
}
|
||||
|
||||
expectMethod('getIndex', 'GET');
|
||||
|
@ -194,7 +194,7 @@ main() {
|
|||
|
||||
test('mounts correct paths', () {
|
||||
void expectPath(String name, String path) {
|
||||
expect(noExposeCtrl.routeMappings[name].path, path);
|
||||
expect(noExposeCtrl.routeMappings[name]!.path, path);
|
||||
}
|
||||
|
||||
expectPath('getIndex', '/');
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:mock_request/mock_request.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
AngelHttp http;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() async {
|
||||
var app = Angel();
|
||||
|
|
|
@ -15,31 +15,31 @@ final String TEXT = "make your bed";
|
|||
final String OVER = "never";
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
http.Client client;
|
||||
HttpServer server;
|
||||
String url;
|
||||
Angel? app;
|
||||
http.Client? client;
|
||||
late HttpServer server;
|
||||
String? url;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
client = http.Client();
|
||||
|
||||
// Inject some todos
|
||||
app.container.registerSingleton(Todo(text: TEXT, over: OVER));
|
||||
app.container.registerFactory<Future<Foo>>((container) async {
|
||||
var req = container.make<RequestContext>();
|
||||
var text = await utf8.decoder.bind(req.body).join();
|
||||
app!.container!.registerSingleton(Todo(text: TEXT, over: OVER));
|
||||
app!.container!.registerFactory<Future<Foo>>((container) async {
|
||||
var req = container.make<RequestContext>()!;
|
||||
var text = await utf8.decoder.bind(req.body!).join();
|
||||
return Foo(text);
|
||||
});
|
||||
|
||||
app.get("/errands", ioc((Todo singleton) => singleton));
|
||||
app.get(
|
||||
app!.get("/errands", ioc((Todo singleton) => singleton));
|
||||
app!.get(
|
||||
"/errands3",
|
||||
ioc(({Errand singleton, Todo foo, RequestContext req}) =>
|
||||
ioc(({required Errand singleton, Todo? foo, RequestContext? req}) =>
|
||||
singleton.text));
|
||||
app.post('/async', ioc((Foo foo) => {'baz': foo.bar}));
|
||||
await app.configure(SingletonController().configureServer);
|
||||
await app.configure(ErrandController().configureServer);
|
||||
app!.post('/async', ioc((Foo foo) => {'baz': foo.bar}));
|
||||
await app!.configure(SingletonController().configureServer);
|
||||
await app!.configure(ErrandController().configureServer);
|
||||
|
||||
server = await AngelHttp(app).startServer();
|
||||
url = "http://${server.address.host}:${server.port}";
|
||||
|
@ -48,7 +48,7 @@ main() {
|
|||
tearDown(() async {
|
||||
app = null;
|
||||
url = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
await server.close(force: true);
|
||||
});
|
||||
|
@ -71,33 +71,33 @@ main() {
|
|||
});
|
||||
|
||||
test("singleton in route", () async {
|
||||
validateTodoSingleton(await client.get(Uri.parse("$url/errands")));
|
||||
validateTodoSingleton(await client!.get(Uri.parse("$url/errands")));
|
||||
});
|
||||
|
||||
test("singleton in controller", () async {
|
||||
validateTodoSingleton(await client.get(Uri.parse("$url/errands2")));
|
||||
validateTodoSingleton(await client!.get(Uri.parse("$url/errands2")));
|
||||
});
|
||||
|
||||
test("make in route", () async {
|
||||
var response = await client.get(Uri.parse("$url/errands3"));
|
||||
var text = await json.decode(response.body) as String;
|
||||
var response = await client!.get(Uri.parse("$url/errands3"));
|
||||
var text = await json.decode(response.body) as String?;
|
||||
expect(text, equals(TEXT));
|
||||
});
|
||||
|
||||
test("make in controller", () async {
|
||||
var response = await client.get(Uri.parse("$url/errands4"));
|
||||
var text = await json.decode(response.body) as String;
|
||||
var response = await client!.get(Uri.parse("$url/errands4"));
|
||||
var text = await json.decode(response.body) as String?;
|
||||
expect(text, equals(TEXT));
|
||||
});
|
||||
|
||||
test('resolve from future in controller', () async {
|
||||
var response =
|
||||
await client.post(Uri.parse('$url/errands4/async'), body: 'hey');
|
||||
await client!.post(Uri.parse('$url/errands4/async'), body: 'hey');
|
||||
expect(response.body, json.encode({'bar': 'hey'}));
|
||||
});
|
||||
|
||||
test('resolve from future in route', () async {
|
||||
var response = await client.post(Uri.parse('$url/async'), body: 'yes');
|
||||
var response = await client!.post(Uri.parse('$url/async'), body: 'yes');
|
||||
expect(response.body, json.encode({'baz': 'yes'}));
|
||||
});
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class Foo {
|
|||
class Errand {
|
||||
Todo todo;
|
||||
|
||||
String get text => todo.text;
|
||||
String? get text => todo.text;
|
||||
|
||||
Errand(this.todo);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ Future<List<int>> getBody(MockHttpResponse rs) async {
|
|||
}
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
late Angel app;
|
||||
|
||||
setUp(() {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
|
@ -43,7 +43,7 @@ main() {
|
|||
void encodingTests(Angel getApp()) {
|
||||
group('encoding', () {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() {
|
||||
app = getApp();
|
||||
|
|
|
@ -7,10 +7,10 @@ import 'package:http/http.dart' as http;
|
|||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
http.Client client;
|
||||
HttpServer server;
|
||||
String url;
|
||||
Angel? app;
|
||||
http.Client? client;
|
||||
late HttpServer server;
|
||||
String? url;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector())
|
||||
|
@ -25,13 +25,13 @@ main() {
|
|||
tearDown(() async {
|
||||
app = null;
|
||||
url = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
await server.close(force: true);
|
||||
});
|
||||
|
||||
test("allow override of method", () async {
|
||||
var response = await client.get(Uri.parse('$url/foo'),
|
||||
var response = await client!.get(Uri.parse('$url/foo'),
|
||||
headers: {'X-HTTP-Method-Override': 'POST'});
|
||||
print('Response: ${response.body}');
|
||||
expect(json.decode(response.body), equals({'hello': 'world'}));
|
||||
|
|
|
@ -6,7 +6,7 @@ main() async {
|
|||
var zone = Zone.current.fork(
|
||||
specification: ZoneSpecification(print: (self, parent, zone, line) {
|
||||
if (line == 'null') {
|
||||
parent.print(zone, cyan.wrap(StackTrace.current.toString()));
|
||||
parent.print(zone, cyan.wrap(StackTrace.current.toString())!);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -13,26 +13,26 @@ main() {
|
|||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
Angel app;
|
||||
HttpServer server;
|
||||
String url;
|
||||
http.Client client;
|
||||
HookedService todoService;
|
||||
Angel? app;
|
||||
late HttpServer server;
|
||||
String? url;
|
||||
http.Client? client;
|
||||
HookedService? todoService;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
client = http.Client();
|
||||
app.use('/todos', MapService());
|
||||
app.use('/books', BookService());
|
||||
app!.use('/todos', MapService());
|
||||
app!.use('/books', BookService());
|
||||
|
||||
todoService = app.findHookedService<MapService>('todos');
|
||||
todoService = app!.findHookedService<MapService>('todos');
|
||||
|
||||
todoService.beforeAllStream().listen((e) {
|
||||
todoService!.beforeAllStream().listen((e) {
|
||||
print('Fired ${e.eventName}! Data: ${e.data}; Params: ${e.params}');
|
||||
});
|
||||
|
||||
app.errorHandler = (e, req, res) {
|
||||
throw e.error;
|
||||
app!.errorHandler = (e, req, res) {
|
||||
throw e.error as Object;
|
||||
};
|
||||
|
||||
server = await AngelHttp(app).startServer();
|
||||
|
@ -43,7 +43,7 @@ main() {
|
|||
await server.close(force: true);
|
||||
app = null;
|
||||
url = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
todoService = null;
|
||||
});
|
||||
|
@ -52,20 +52,20 @@ main() {
|
|||
int count = 0;
|
||||
|
||||
todoService
|
||||
..beforeIndexed.listen((_) {
|
||||
?..beforeIndexed.listen((_) {
|
||||
count++;
|
||||
})
|
||||
..afterIndexed.listen((_) {
|
||||
count++;
|
||||
});
|
||||
|
||||
var response = await client.get(Uri.parse("$url/todos"));
|
||||
var response = await client!.get(Uri.parse("$url/todos"));
|
||||
print(response.body);
|
||||
expect(count, equals(2));
|
||||
});
|
||||
|
||||
test("cancel before", () async {
|
||||
todoService.beforeCreated
|
||||
todoService!.beforeCreated
|
||||
..listen((HookedServiceEvent event) {
|
||||
event.cancel({"hello": "hooked world"});
|
||||
})
|
||||
|
@ -73,7 +73,7 @@ main() {
|
|||
event.cancel({"this_hook": "should never run"});
|
||||
});
|
||||
|
||||
var response = await client.post(Uri.parse("$url/todos"),
|
||||
var response = await client!.post(Uri.parse("$url/todos"),
|
||||
body: json.encode({"arbitrary": "data"}),
|
||||
headers: headers as Map<String, String>);
|
||||
print(response.body);
|
||||
|
@ -82,7 +82,7 @@ main() {
|
|||
});
|
||||
|
||||
test("cancel after", () async {
|
||||
todoService.afterIndexed
|
||||
todoService!.afterIndexed
|
||||
..listen((HookedServiceEvent event) async {
|
||||
// Hooks can be Futures ;)
|
||||
event.cancel([
|
||||
|
@ -93,20 +93,20 @@ main() {
|
|||
event.cancel({"this_hook": "should never run either"});
|
||||
});
|
||||
|
||||
var response = await client.get(Uri.parse("$url/todos"));
|
||||
var response = await client!.get(Uri.parse("$url/todos"));
|
||||
print(response.body);
|
||||
var result = json.decode(response.body) as List;
|
||||
expect(result[0]["angel"], equals("framework"));
|
||||
});
|
||||
|
||||
test('asStream() fires', () async {
|
||||
var stream = todoService.afterCreated.asStream();
|
||||
await todoService.create({'angel': 'framework'});
|
||||
var stream = todoService!.afterCreated.asStream();
|
||||
await todoService!.create({'angel': 'framework'});
|
||||
expect(await stream.first.then((e) => e.result['angel']), 'framework');
|
||||
});
|
||||
|
||||
test('metadata', () async {
|
||||
final service = HookedService(IncrementService())..addHooks(app);
|
||||
final service = HookedService(IncrementService())..addHooks(app!);
|
||||
expect(service.inner, isNot(const IsInstanceOf<MapService>()));
|
||||
IncrementService.TIMES = 0;
|
||||
await service.index();
|
||||
|
@ -114,14 +114,15 @@ main() {
|
|||
});
|
||||
|
||||
test('inject request + response', () async {
|
||||
HookedService books = app.findService('books');
|
||||
HookedService books = app!.findService('books')
|
||||
as HookedService<dynamic, dynamic, Service<dynamic, dynamic>>;
|
||||
|
||||
books.beforeIndexed.listen((e) {
|
||||
expect([e.request, e.response], everyElement(isNotNull));
|
||||
print('Indexing books at path: ${e.request.path}');
|
||||
print('Indexing books at path: ${e.request!.path}');
|
||||
});
|
||||
|
||||
var response = await client.get(Uri.parse('$url/books'));
|
||||
var response = await client!.get(Uri.parse('$url/books'));
|
||||
print(response.body);
|
||||
|
||||
var result = json.decode(response.body);
|
||||
|
@ -137,8 +138,8 @@ main() {
|
|||
var type = e.isBefore ? 'before' : 'after';
|
||||
print('Params to $type ${e.eventName}: ${e.params}');
|
||||
expect(e.params, isMap);
|
||||
expect(e.params.keys, contains('provider'));
|
||||
expect(e.params['provider'], const IsInstanceOf<Providers>());
|
||||
expect(e.params!.keys, contains('provider'));
|
||||
expect(e.params!['provider'], const IsInstanceOf<Providers>());
|
||||
}
|
||||
|
||||
svc
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||
import 'package:angel_container/mirrors.dart';
|
||||
import 'package:angel_framework/angel_framework.dart' hide Header;
|
||||
import 'package:angel_framework/http2.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http/src/multipart_file.dart' as http;
|
||||
import 'package:http/src/multipart_request.dart' as http;
|
||||
import 'package:http/io_client.dart';
|
||||
|
@ -22,10 +23,10 @@ Stream<List<int>> jfkStream() {
|
|||
|
||||
void main() {
|
||||
var client = Http2Client();
|
||||
IOClient h1c;
|
||||
late IOClient h1c;
|
||||
Angel app;
|
||||
AngelHttp2 http2;
|
||||
Uri serverRoot;
|
||||
late AngelHttp2 http2;
|
||||
late Uri serverRoot;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector())..encoders['gzip'] = gzip.encoder;
|
||||
|
@ -63,11 +64,13 @@ void main() {
|
|||
|
||||
app.post('/upload', (req, res) async {
|
||||
await req.parseBody();
|
||||
var body = req.bodyAsMap, files = req.uploadedFiles;
|
||||
var file = files.firstWhere((f) => f.name == 'file');
|
||||
var body = req.bodyAsMap;
|
||||
List<UploadedFile> files = req.uploadedFiles ?? [];
|
||||
|
||||
UploadedFile file = files.firstWhereOrNull((f) => f.name == 'file')!;
|
||||
return [
|
||||
await file.data.map((l) => l.length).reduce((a, b) => a + b),
|
||||
file.contentType.mimeType,
|
||||
file.contentType!.mimeType,
|
||||
body
|
||||
];
|
||||
});
|
||||
|
@ -103,13 +106,13 @@ void main() {
|
|||
|
||||
http2 = AngelHttp2(app, ctx, allowHttp1: true);
|
||||
|
||||
var server = await http2.startServer();
|
||||
SecureServerSocket server = await http2.startServer();
|
||||
serverRoot = Uri.parse('https://127.0.0.1:${server.port}');
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await http2.close();
|
||||
await h1c.close();
|
||||
h1c.close();
|
||||
});
|
||||
|
||||
test('buffered response', () async {
|
||||
|
@ -167,7 +170,7 @@ void main() {
|
|||
test('json response', () async {
|
||||
var response = await client.get(serverRoot.replace(path: '/json'));
|
||||
expect(response.body, json.encode({'foo': 'bar'}));
|
||||
expect(ContentType.parse(response.headers['content-type']).mimeType,
|
||||
expect(ContentType.parse(response.headers['content-type']!).mimeType,
|
||||
ContentType.json.mimeType);
|
||||
});
|
||||
|
||||
|
|
|
@ -89,11 +89,11 @@ class Http2Client extends BaseClient {
|
|||
var closed = await readResponse(stream, headers, body);
|
||||
return StreamedResponse(
|
||||
Stream.fromIterable([body.takeBytes()]),
|
||||
int.parse(headers[':status']),
|
||||
int.parse(headers[':status']!),
|
||||
headers: headers,
|
||||
isRedirect: headers.containsKey('location'),
|
||||
contentLength: headers.containsKey('content-length')
|
||||
? int.parse(headers['content-length'])
|
||||
? int.parse(headers['content-length']!)
|
||||
: null,
|
||||
request: request,
|
||||
reasonPhrase: null,
|
||||
|
|
|
@ -24,7 +24,7 @@ void main() {
|
|||
|
||||
parameterMetaTests() {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
|
|
|
@ -9,8 +9,8 @@ import 'package:mock_request/mock_request.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
late Angel app;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() {
|
||||
app = Angel(reflector: MirrorsReflector())
|
||||
|
|
|
@ -7,10 +7,10 @@ import 'package:test/test.dart';
|
|||
import 'pretty_log.dart';
|
||||
|
||||
void main() {
|
||||
http.IOClient client;
|
||||
AngelHttp driver;
|
||||
Logger logger;
|
||||
StringBuffer buf;
|
||||
late http.IOClient client;
|
||||
late AngelHttp driver;
|
||||
late Logger logger;
|
||||
late StringBuffer buf;
|
||||
|
||||
setUp(() async {
|
||||
buf = StringBuffer();
|
||||
|
|
|
@ -20,7 +20,7 @@ testMiddlewareMetadata(RequestContext req, ResponseContext res) async {
|
|||
class QueryService extends Service {
|
||||
@override
|
||||
@Middleware([interceptor])
|
||||
read(id, [Map params]) async => params;
|
||||
read(id, [Map? params]) async => params;
|
||||
}
|
||||
|
||||
void interceptor(RequestContext req, ResponseContext res) {
|
||||
|
@ -35,19 +35,19 @@ bool interceptService(RequestContext req, ResponseContext res) {
|
|||
}
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
Angel nested;
|
||||
Angel todos;
|
||||
String url;
|
||||
http.Client client;
|
||||
Angel? app;
|
||||
Angel? nested;
|
||||
Angel? todos;
|
||||
String? url;
|
||||
http.Client? client;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
nested = Angel(reflector: MirrorsReflector());
|
||||
todos = Angel(reflector: MirrorsReflector());
|
||||
|
||||
[app, nested, todos].forEach((Angel app) {
|
||||
app.logger = Logger('routing_test')
|
||||
[app, nested, todos].forEach((Angel? app) {
|
||||
app!.logger = Logger('routing_test')
|
||||
..onRecord.listen((rec) {
|
||||
if (rec.error != null) {
|
||||
stdout
|
||||
|
@ -58,44 +58,44 @@ main() {
|
|||
});
|
||||
});
|
||||
|
||||
todos.get('/action/:action', (req, res) => res.json(req.params));
|
||||
todos!.get('/action/:action', (req, res) => res.json(req.params));
|
||||
|
||||
Route ted;
|
||||
late Route ted;
|
||||
|
||||
ted = nested.post('/ted/:route', (RequestContext req, res) {
|
||||
ted = nested!.post('/ted/:route', (RequestContext req, res) {
|
||||
print('Params: ${req.params}');
|
||||
print('Path: ${ted.path}, uri: ${req.path}');
|
||||
print('matcher: ${ted.parser}');
|
||||
return req.params;
|
||||
});
|
||||
|
||||
app.mount('/nes', nested);
|
||||
app.get('/meta', testMiddlewareMetadata);
|
||||
app.get('/intercepted', (req, res) => 'This should not be shown',
|
||||
app!.mount('/nes', nested!);
|
||||
app!.get('/meta', testMiddlewareMetadata);
|
||||
app!.get('/intercepted', (req, res) => 'This should not be shown',
|
||||
middleware: [interceptor]);
|
||||
app.get('/hello', (req, res) => 'world');
|
||||
app.get('/name/:first/last/:last', (req, res) => req.params);
|
||||
app.post(
|
||||
app!.get('/hello', (req, res) => 'world');
|
||||
app!.get('/name/:first/last/:last', (req, res) => req.params);
|
||||
app!.post(
|
||||
'/lambda',
|
||||
(RequestContext req, res) =>
|
||||
req.parseBody().then((_) => req.bodyAsMap));
|
||||
app.mount('/todos/:id', todos);
|
||||
app
|
||||
app!.mount('/todos/:id', todos!);
|
||||
app!
|
||||
.get('/greet/:name',
|
||||
(RequestContext req, res) async => "Hello ${req.params['name']}")
|
||||
.name = 'Named routes';
|
||||
app.get('/named', (req, ResponseContext res) async {
|
||||
app!.get('/named', (req, ResponseContext res) async {
|
||||
await res.redirectTo('Named routes', {'name': 'tests'});
|
||||
});
|
||||
app.get('/log', (RequestContext req, res) async {
|
||||
app!.get('/log', (RequestContext req, res) async {
|
||||
print("Query: ${req.queryParameters}");
|
||||
return "Logged";
|
||||
});
|
||||
|
||||
app.get('/method', (req, res) => 'Only GET');
|
||||
app.post('/method', (req, res) => 'Only POST');
|
||||
app!.get('/method', (req, res) => 'Only GET');
|
||||
app!.post('/method', (req, res) => 'Only POST');
|
||||
|
||||
app.use('/query', QueryService());
|
||||
app!.use('/query', QueryService());
|
||||
|
||||
RequestHandler write(String message) {
|
||||
return (req, res) {
|
||||
|
@ -104,35 +104,35 @@ main() {
|
|||
};
|
||||
}
|
||||
|
||||
app.chain([write('a')]).chain([write('b'), write('c')]).get(
|
||||
app!.chain([write('a')]).chain([write('b'), write('c')]).get(
|
||||
'/chained', (req, res) => res.close());
|
||||
|
||||
app.fallback((req, res) => 'MJ');
|
||||
app!.fallback((req, res) => 'MJ');
|
||||
|
||||
//app.dumpTree(header: "DUMPING ROUTES:", showMatchers: true);
|
||||
|
||||
client = http.Client();
|
||||
var server = await AngelHttp(app).startServer('127.0.0.1', 0);
|
||||
HttpServer server = await AngelHttp(app).startServer('127.0.0.1', 0);
|
||||
url = "http://${server.address.host}:${server.port}";
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await app.close();
|
||||
await app!.close();
|
||||
app = null;
|
||||
nested = null;
|
||||
todos = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
url = null;
|
||||
});
|
||||
|
||||
test('Can match basic url', () async {
|
||||
var response = await client.get(Uri.parse("$url/hello"));
|
||||
var response = await client!.get(Uri.parse("$url/hello"));
|
||||
expect(response.body, equals('"world"'));
|
||||
});
|
||||
|
||||
test('Can match url with multiple parameters', () async {
|
||||
var response = await client.get(Uri.parse('$url/name/HELLO/last/WORLD'));
|
||||
var response = await client!.get(Uri.parse('$url/name/HELLO/last/WORLD'));
|
||||
print('Response: ${response.body}');
|
||||
var json_ = json.decode(response.body);
|
||||
expect(json_, const IsInstanceOf<Map>());
|
||||
|
@ -141,18 +141,18 @@ main() {
|
|||
});
|
||||
|
||||
test('Chained routes', () async {
|
||||
var response = await client.get(Uri.parse("$url/chained"));
|
||||
var response = await client!.get(Uri.parse("$url/chained"));
|
||||
expect(response.body, equals('abc'));
|
||||
});
|
||||
|
||||
test('Can nest another Angel instance', () async {
|
||||
var response = await client.post(Uri.parse('$url/nes/ted/foo'));
|
||||
var response = await client!.post(Uri.parse('$url/nes/ted/foo'));
|
||||
var json_ = json.decode(response.body);
|
||||
expect(json_['route'], equals('foo'));
|
||||
});
|
||||
|
||||
test('Can parse parameters from a nested Angel instance', () async {
|
||||
var response = await client.get(Uri.parse('$url/todos/1337/action/test'));
|
||||
var response = await client!.get(Uri.parse('$url/todos/1337/action/test'));
|
||||
var json_ = json.decode(response.body);
|
||||
print('JSON: $json_');
|
||||
expect(json_['id'], equals('1337'));
|
||||
|
@ -160,32 +160,32 @@ main() {
|
|||
});
|
||||
|
||||
test('Can add and use named middleware', () async {
|
||||
var response = await client.get(Uri.parse('$url/intercepted'));
|
||||
var response = await client!.get(Uri.parse('$url/intercepted'));
|
||||
expect(response.body, equals('Middleware'));
|
||||
});
|
||||
|
||||
test('Middleware via metadata', () async {
|
||||
// Metadata
|
||||
var response = await client.get(Uri.parse('$url/meta'));
|
||||
var response = await client!.get(Uri.parse('$url/meta'));
|
||||
expect(response.body, equals('Middleware'));
|
||||
});
|
||||
|
||||
test('Can serialize function result as JSON', () async {
|
||||
Map headers = <String, String>{'Content-Type': 'application/json'};
|
||||
String postData = json.encode({'it': 'works'});
|
||||
var response = await client.post(Uri.parse("$url/lambda"),
|
||||
var response = await client!.post(Uri.parse("$url/lambda"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
print('Response: ${response.body}');
|
||||
expect(json.decode(response.body)['it'], equals('works'));
|
||||
});
|
||||
|
||||
test('Fallback routes', () async {
|
||||
var response = await client.get(Uri.parse('$url/my_favorite_artist'));
|
||||
var response = await client!.get(Uri.parse('$url/my_favorite_artist'));
|
||||
expect(response.body, equals('"MJ"'));
|
||||
});
|
||||
|
||||
test('Can name routes', () {
|
||||
Route foo = app.get('/framework/:id', null)..name = 'frm';
|
||||
Route foo = app!.get('/framework/:id', null)..name = 'frm';
|
||||
print('Foo: $foo');
|
||||
String uri = foo.makeUri({'id': 'angel'});
|
||||
print(uri);
|
||||
|
@ -193,32 +193,32 @@ main() {
|
|||
});
|
||||
|
||||
test('Redirect to named routes', () async {
|
||||
var response = await client.get(Uri.parse('$url/named'));
|
||||
var response = await client!.get(Uri.parse('$url/named'));
|
||||
print(response.body);
|
||||
expect(json.decode(response.body), equals('Hello tests'));
|
||||
});
|
||||
|
||||
test('Match routes, even with query params', () async {
|
||||
var response = await client
|
||||
var response = await client!
|
||||
.get(Uri.parse("$url/log?foo=bar&bar=baz&baz.foo=bar&baz.bar=foo"));
|
||||
print(response.body);
|
||||
expect(json.decode(response.body), equals('Logged'));
|
||||
|
||||
response = await client.get(Uri.parse("$url/query/foo?bar=baz"));
|
||||
response = await client!.get(Uri.parse("$url/query/foo?bar=baz"));
|
||||
print(response.body);
|
||||
expect(response.body, equals("Service with Middleware"));
|
||||
});
|
||||
|
||||
test('only match route with matching method', () async {
|
||||
var response = await client.get(Uri.parse("$url/method"));
|
||||
var response = await client!.get(Uri.parse("$url/method"));
|
||||
print(response.body);
|
||||
expect(response.body, '"Only GET"');
|
||||
|
||||
response = await client.post(Uri.parse("$url/method"));
|
||||
response = await client!.post(Uri.parse("$url/method"));
|
||||
print(response.body);
|
||||
expect(response.body, '"Only POST"');
|
||||
|
||||
response = await client.patch(Uri.parse("$url/method"));
|
||||
response = await client!.patch(Uri.parse("$url/method"));
|
||||
print(response.body);
|
||||
expect(response.body, '"MJ"');
|
||||
});
|
||||
|
|
|
@ -8,10 +8,10 @@ import 'package:http_parser/http_parser.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
http.Client client;
|
||||
HttpServer server;
|
||||
String url;
|
||||
Angel? app;
|
||||
http.Client? client;
|
||||
late HttpServer server;
|
||||
String? url;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector())
|
||||
|
@ -29,17 +29,17 @@ main() {
|
|||
tearDown(() async {
|
||||
app = null;
|
||||
url = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
await server.close(force: true);
|
||||
});
|
||||
|
||||
test("correct content-type", () async {
|
||||
var response = await client.get(Uri.parse('$url/foo'));
|
||||
var response = await client!.get(Uri.parse('$url/foo'));
|
||||
print('Response: ${response.body}');
|
||||
expect(response.headers['content-type'], contains('application/json'));
|
||||
|
||||
response = await client.get(Uri.parse('$url/bar'));
|
||||
response = await client!.get(Uri.parse('$url/bar'));
|
||||
print('Response: ${response.body}');
|
||||
expect(response.headers['content-type'], contains('text/html'));
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ main() {
|
|||
await app.errorHandler(e, req, res);
|
||||
await http.sendResponse(rq, rs, req, res);
|
||||
expect(
|
||||
ContentType.parse(rs.headers.value('content-type')).mimeType,
|
||||
ContentType.parse(rs.headers.value('content-type')!).mimeType,
|
||||
'text/html',
|
||||
);
|
||||
expect(rs.statusCode, e.statusCode);
|
||||
|
@ -116,8 +116,8 @@ main() {
|
|||
var http = AngelHttp(app);
|
||||
app.responseFinalizers
|
||||
.add((req, res) => throw AngelHttpException.forbidden());
|
||||
RequestContext req;
|
||||
ResponseContext res;
|
||||
late RequestContext req;
|
||||
late ResponseContext res;
|
||||
|
||||
setUp(() async {
|
||||
var rq = MockHttpRequest('GET', $foo);
|
||||
|
@ -155,8 +155,8 @@ main() {
|
|||
});
|
||||
|
||||
group('handleAngelHttpException', () {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
late Angel app;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
|
@ -182,7 +182,7 @@ main() {
|
|||
(http.handleRequest(rq));
|
||||
await rq.response.toList();
|
||||
expect(rq.response.statusCode, 403);
|
||||
expect(rq.response.headers.contentType.mimeType, 'application/json');
|
||||
expect(rq.response.headers.contentType!.mimeType, 'application/json');
|
||||
});
|
||||
|
||||
test('can throw in finalizer', () async {
|
||||
|
@ -192,7 +192,7 @@ main() {
|
|||
(http.handleRequest(rq));
|
||||
await rq.response.toList();
|
||||
expect(rq.response.statusCode, 403);
|
||||
expect(rq.response.headers.contentType.mimeType, 'application/json');
|
||||
expect(rq.response.headers.contentType!.mimeType, 'application/json');
|
||||
});
|
||||
|
||||
test('can send html', () async {
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:test/test.dart';
|
|||
|
||||
void main() {
|
||||
MapService inner;
|
||||
Service<String, Todo> mapped;
|
||||
late Service<String?, Todo> mapped;
|
||||
|
||||
setUp(() {
|
||||
inner = MapService();
|
||||
|
@ -22,8 +22,8 @@ void main() {
|
|||
});
|
||||
|
||||
group('after create', () {
|
||||
Todo result;
|
||||
String id;
|
||||
late Todo result;
|
||||
String? id;
|
||||
|
||||
setUp(() async {
|
||||
result = await mapped.create(Todo(text: 'hello', complete: false));
|
||||
|
@ -55,16 +55,16 @@ void main() {
|
|||
}
|
||||
|
||||
class Todo {
|
||||
final String id, text;
|
||||
final bool complete;
|
||||
final String? id, text;
|
||||
final bool? complete;
|
||||
|
||||
Todo({this.id, this.text, this.complete});
|
||||
|
||||
static Todo fromMap(Map<String, dynamic> json) {
|
||||
return Todo(
|
||||
id: json['id'] as String,
|
||||
text: json['text'] as String,
|
||||
complete: json['complete'] as bool);
|
||||
id: json['id'] as String?,
|
||||
text: json['text'] as String?,
|
||||
complete: json['complete'] as bool?);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(Todo model) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:angel_container/mirrors.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
|
@ -7,8 +9,8 @@ import 'package:stack_trace/stack_trace.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
class Todo extends Model {
|
||||
String text;
|
||||
String over;
|
||||
String? text;
|
||||
String? over;
|
||||
}
|
||||
|
||||
main() {
|
||||
|
@ -16,35 +18,35 @@ main() {
|
|||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
Angel app;
|
||||
MapService service;
|
||||
String url;
|
||||
http.Client client;
|
||||
Angel? app;
|
||||
late MapService service;
|
||||
String? url;
|
||||
http.Client? client;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel(reflector: MirrorsReflector())
|
||||
..use('/todos', service = MapService())
|
||||
..errorHandler = (e, req, res) {
|
||||
if (e.error != null) print('Whoops: ${e.error}');
|
||||
if (e.stackTrace != null) print(Chain.forTrace(e.stackTrace).terse);
|
||||
if (e.stackTrace != null) print(Chain.forTrace(e.stackTrace!).terse);
|
||||
};
|
||||
|
||||
var server = await AngelHttp(app).startServer();
|
||||
HttpServer server = await AngelHttp(app).startServer();
|
||||
client = http.Client();
|
||||
url = "http://${server.address.host}:${server.port}";
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await app.close();
|
||||
await app!.close();
|
||||
app = null;
|
||||
url = null;
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
});
|
||||
|
||||
group('memory', () {
|
||||
test('can index an empty service', () async {
|
||||
var response = await client.get(Uri.parse("$url/todos/"));
|
||||
var response = await client!.get(Uri.parse("$url/todos/"));
|
||||
print(response.body);
|
||||
expect(response.body, equals('[]'));
|
||||
print(response.body);
|
||||
|
@ -53,7 +55,7 @@ main() {
|
|||
|
||||
test('can create data', () async {
|
||||
String postData = json.encode({'text': 'Hello, world!'});
|
||||
var response = await client.post(Uri.parse("$url/todos"),
|
||||
var response = await client!.post(Uri.parse("$url/todos"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
expect(response.statusCode, 201);
|
||||
var jsons = json.decode(response.body);
|
||||
|
@ -63,9 +65,9 @@ main() {
|
|||
|
||||
test('can fetch data', () async {
|
||||
String postData = json.encode({'text': 'Hello, world!'});
|
||||
await client.post(Uri.parse("$url/todos"),
|
||||
await client!.post(Uri.parse("$url/todos"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
var response = await client.get(Uri.parse("$url/todos/0"));
|
||||
var response = await client!.get(Uri.parse("$url/todos/0"));
|
||||
expect(response.statusCode, 200);
|
||||
var jsons = json.decode(response.body);
|
||||
print(jsons);
|
||||
|
@ -74,12 +76,12 @@ main() {
|
|||
|
||||
test('can modify data', () async {
|
||||
String postData = json.encode({'text': 'Hello, world!'});
|
||||
await client.post(Uri.parse("$url/todos"),
|
||||
await client!.post(Uri.parse("$url/todos"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
postData = json.encode({'text': 'modified'});
|
||||
|
||||
var response = await client.patch(Uri.parse("$url/todos/0"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
var response = await client!
|
||||
.patch(Uri.parse("$url/todos/0"), headers: headers, body: postData);
|
||||
expect(response.statusCode, 200);
|
||||
var jsons = json.decode(response.body);
|
||||
print(jsons);
|
||||
|
@ -88,12 +90,12 @@ main() {
|
|||
|
||||
test('can overwrite data', () async {
|
||||
String postData = json.encode({'text': 'Hello, world!'});
|
||||
await client.post(Uri.parse("$url/todos"),
|
||||
await client!.post(Uri.parse("$url/todos"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
postData = json.encode({'over': 'write'});
|
||||
|
||||
var response = await client.post(Uri.parse("$url/todos/0"),
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
var response = await client!
|
||||
.post(Uri.parse("$url/todos/0"), headers: headers, body: postData);
|
||||
expect(response.statusCode, 200);
|
||||
var jsons = json.decode(response.body);
|
||||
print(jsons);
|
||||
|
@ -108,18 +110,18 @@ main() {
|
|||
await service.create({'baz': 'quux'})
|
||||
];
|
||||
|
||||
var ids = items.map((m) => m['id'] as String).toList();
|
||||
var ids = items.map((m) => m['id'] as String?).toList();
|
||||
expect(await service.readMany(ids), items);
|
||||
});
|
||||
|
||||
test('can delete data', () async {
|
||||
String postData = json.encode({'text': 'Hello, world!'});
|
||||
var created = await client
|
||||
var created = await client!
|
||||
.post(Uri.parse("$url/todos"),
|
||||
headers: headers as Map<String, String>, body: postData)
|
||||
.then((r) => json.decode(r.body));
|
||||
var response =
|
||||
await client.delete(Uri.parse("$url/todos/${created['id']}"));
|
||||
await client!.delete(Uri.parse("$url/todos/${created['id']}"));
|
||||
expect(response.statusCode, 200);
|
||||
var json_ = json.decode(response.body);
|
||||
print(json_);
|
||||
|
@ -127,7 +129,7 @@ main() {
|
|||
});
|
||||
|
||||
test('cannot remove all unless explicitly set', () async {
|
||||
var response = await client.delete(Uri.parse('$url/todos/null'));
|
||||
var response = await client!.delete(Uri.parse('$url/todos/null'));
|
||||
expect(response.statusCode, 403);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'package:test/test.dart';
|
|||
import 'encoders_buffer_test.dart' show encodingTests;
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
late Angel app;
|
||||
late AngelHttp http;
|
||||
|
||||
setUp(() {
|
||||
app = Angel(reflector: MirrorsReflector());
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:test/test.dart';
|
|||
main() {
|
||||
test('default view generator', () async {
|
||||
var app = Angel();
|
||||
var view = await app.viewGenerator('foo', {'bar': 'baz'});
|
||||
var view = await app.viewGenerator!('foo', {'bar': 'baz'});
|
||||
expect(view, contains('No view engine'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue