platform/packages/shelf/lib/src/shelf_response.dart

144 lines
3.6 KiB
Dart
Raw Normal View History

2019-10-14 15:39:43 +00:00
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<ShelfResponseContext> {
2021-06-20 12:37:20 +00:00
@override
2019-10-14 15:39:43 +00:00
final Angel app;
2021-06-20 12:37:20 +00:00
2019-10-14 15:39:43 +00:00
final StreamController<List<int>> _ctrl = StreamController();
2021-06-20 12:37:20 +00:00
bool _isOpen = true;
bool _isDetached = false;
final bool _wasClosedByHandler = false;
bool _handlersAreDone = false;
2019-10-14 15:39:43 +00:00
2019-10-14 15:54:55 +00:00
ShelfResponseContext(this.app);
2021-06-20 12:37:20 +00:00
ShelfRequestContext? _correspondingRequest;
2019-10-14 15:54:55 +00:00
2019-10-14 16:38:30 +00:00
bool get wasClosedByHandler => _wasClosedByHandler;
void closeSilently() => _handlersAreDone = true;
2021-06-20 12:37:20 +00:00
@override
ShelfRequestContext? get correspondingRequest => _correspondingRequest;
2019-10-14 15:54:55 +00:00
2021-06-20 12:37:20 +00:00
set correspondingRequest(ShelfRequestContext? value) {
2019-10-14 15:54:55 +00:00
if (_correspondingRequest == null) {
_correspondingRequest = value;
} else {
throw StateError('The corresponding request has already been assigned.');
}
}
2019-10-14 15:39:43 +00:00
shelf.Response get shelfResponse {
return shelf.Response(statusCode, body: _ctrl.stream, headers: headers);
}
@override
Future<void> close() {
2019-10-14 16:38:30 +00:00
if (!_handlersAreDone) {
_isOpen = false;
}
2019-10-14 15:39:43 +00:00
_ctrl.close();
return super.close();
}
2021-06-20 12:37:20 +00:00
Iterable<String>? __allowedEncodings;
2019-10-14 15:39:43 +00:00
2021-06-20 12:37:20 +00:00
Iterable<String>? get _allowedEncodings {
return __allowedEncodings ??= correspondingRequest!.headers
2019-10-14 15:39:43 +00:00
.value('accept-encoding')
?.split(',')
2021-06-20 12:37:20 +00:00
.map((s) => s.trim())
.where((s) => s.isNotEmpty)
.map((str) {
2019-10-14 15:39:43 +00:00
// Ignore quality specifications in accept-encoding
// ex. gzip;q=0.8
if (!str.contains(';')) return str;
return str.split(';')[0];
});
}
@override
Future addStream(Stream<List<int>> stream) {
if (!isOpen && isBuffered) throw ResponseContext.closed();
2021-06-20 12:37:20 +00:00
var output = stream;
2019-10-14 15:39:43 +00:00
if (encoders.isNotEmpty && correspondingRequest != null) {
if (_allowedEncodings != null) {
2021-06-20 12:37:20 +00:00
for (var encodingName in _allowedEncodings!) {
Converter<List<int>, List<int>>? encoder;
var key = encodingName;
2019-10-14 15:39:43 +00:00
if (encoders.containsKey(encodingName)) {
encoder = encoders[encodingName];
} else if (encodingName == '*') {
encoder = encoders[key = encoders.keys.first];
}
if (encoder != null) {
2021-06-20 12:37:20 +00:00
output = encoders[key]!.bind(output);
2019-10-14 15:39:43 +00:00
break;
}
}
}
}
return _ctrl.addStream(output);
}
@override
void add(List<int> data) {
if (!isOpen && isBuffered) {
throw ResponseContext.closed();
} else if (_isOpen) {
if (encoders.isNotEmpty && correspondingRequest != null) {
if (_allowedEncodings != null) {
2021-06-20 12:37:20 +00:00
for (var encodingName in _allowedEncodings!) {
Converter<List<int>, List<int>>? encoder;
var key = encodingName;
2019-10-14 15:39:43 +00:00
if (encoders.containsKey(encodingName)) {
encoder = encoders[encodingName];
} else if (encodingName == '*') {
encoder = encoders[key = encoders.keys.first];
}
if (encoder != null) {
2021-06-20 12:37:20 +00:00
data = encoders[key]!.convert(data);
2019-10-14 15:39:43 +00:00
break;
}
}
}
}
_ctrl.add(data);
}
}
@override
2021-06-20 12:37:20 +00:00
BytesBuilder? get buffer => null;
2019-10-14 15:39:43 +00:00
@override
FutureOr<ShelfResponseContext> detach() {
_isDetached = true;
return this;
}
@override
bool get isBuffered => false;
@override
bool get isOpen => _isOpen && !_isDetached;
@override
void useBuffer() {}
@override
ShelfResponseContext get rawResponse => this;
}