dvRunMiddlewares function

Future<DVMiddlewareResult> dvRunMiddlewares(
  1. List<String> keys,
  2. Object? request
)

Runs a route's declared middleware in the order it declared them.

Throws when a key nothing implements reaches this: the generator refuses such a build, and a request arriving with one anyway means a generated file is older than the framework running it. Serving it as though the middleware applied is the failure this whole file exists to end.

Implementation

Future<DVMiddlewareResult> dvRunMiddlewares(
  List<String> keys,
  Object? request,
) async {
  final MiddlewareChain chain = MiddlewareChain();
  for (final String key in keys) {
    if (dvMiddlewareKeysAlwaysOn.contains(key)) continue;
    // Enforced by the generated prelude, before this chain is reached.
    if (dvMiddlewareKeysAtRequest.contains(key)) continue;
    // Wrapped around the handler and this chain both.
    if (dvMiddlewareKeysWrapping.contains(key)) continue;
    final Middleware? middleware = dvMiddlewareFor(key);
    if (middleware == null) {
      throw ArgumentError.value(
        key,
        'middleware',
        dvMiddlewareKeysUnbuiltReason[key] ??
            'is not a middleware this version of Dartvel implements.',
      );
    }
    chain.use(middleware);
  }

  final MiddlewareContext context = await chain.execute(request);

  for (final MapEntry<String, int> refusal in _refusalStatus.entries) {
    if (!context.data.containsKey(refusal.key)) continue;
    return DVMiddlewareResult(
      allowed: false,
      status: refusal.value,
      message: _refusalMessage[refusal.key]!,
      // Nothing from a refused chain decorates a response that is not being
      // sent, except when to come back after a rate limit (RFC 9110 10.2.3).
      headers: <String, String>{
        if (refusal.key == 'rateLimitError' && context.data['retryAfter'] is int)
          'Retry-After': '${context.data['retryAfter']}',
      },
      data: const <String, Object?>{},
    );
  }

  final Map<String, String> headers = <String, String>{};
  final Object? csp = context.data['cspHeader'];
  if (csp is String) headers['Content-Security-Policy'] = csp;
  final Object? security = context.data['securityHeaders'];
  if (security is Map) {
    for (final MapEntry<Object?, Object?> entry in security.entries) {
      final Object? name = entry.key;
      final Object? value = entry.value;
      if (name is String && value is String) headers[name] = value;
    }
  }

  return DVMiddlewareResult(
    allowed: context.shouldContinue,
    status: 200,
    message: '',
    headers: headers,
    data: Map<String, Object?>.unmodifiable(context.data),
  );
}