import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:angel_framework/angel_framework.dart'; import 'package:shelf/shelf.dart' as shelf; import 'shelf_request.dart'; class ShelfResponseContext extends ResponseContext { final Angel app; final ShelfRequestContext correspondingRequest; final StreamController> _ctrl = StreamController(); bool _isOpen = true, _isDetached = false; ShelfResponseContext(this.app, this.correspondingRequest); shelf.Response get shelfResponse { return shelf.Response(statusCode, body: _ctrl.stream, headers: headers); } @override Future close() { _isOpen = false; _ctrl.close(); return super.close(); } Iterable __allowedEncodings; Iterable get _allowedEncodings { return __allowedEncodings ??= correspondingRequest.headers .value('accept-encoding') ?.split(',') ?.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; return str.split(';')[0]; }); } @override Future addStream(Stream> stream) { if (!isOpen && isBuffered) throw ResponseContext.closed(); Stream> output = stream; if (encoders.isNotEmpty && correspondingRequest != null) { if (_allowedEncodings != null) { for (var encodingName in _allowedEncodings) { Converter, List> encoder; String key = encodingName; if (encoders.containsKey(encodingName)) { encoder = encoders[encodingName]; } else if (encodingName == '*') { encoder = encoders[key = encoders.keys.first]; } if (encoder != null) { output = encoders[key].bind(output); break; } } } } return _ctrl.addStream(output); } @override void add(List data) { if (!isOpen && isBuffered) { throw ResponseContext.closed(); } else if (_isOpen) { if (encoders.isNotEmpty && correspondingRequest != null) { if (_allowedEncodings != null) { for (var encodingName in _allowedEncodings) { Converter, List> encoder; String key = encodingName; if (encoders.containsKey(encodingName)) { encoder = encoders[encodingName]; } else if (encodingName == '*') { encoder = encoders[key = encoders.keys.first]; } if (encoder != null) { data = encoders[key].convert(data); break; } } } } _ctrl.add(data); } } @override BytesBuilder get buffer => null; @override FutureOr detach() { _isDetached = true; return this; } @override bool get isBuffered => false; @override bool get isOpen => _isOpen && !_isDetached; @override void useBuffer() {} @override ShelfResponseContext get rawResponse => this; }