platform/packages/wings/lib/src/wings_response.dart

216 lines
5.5 KiB
Dart
Raw Normal View History

import 'dart:async';
2019-04-29 08:22:36 +00:00
import 'dart:convert';
import 'dart:io';
2019-04-29 08:22:36 +00:00
import 'dart:typed_data';
import 'package:angel_framework/angel_framework.dart';
2019-04-29 08:22:36 +00:00
import 'package:charcode/ascii.dart';
import 'wings_request.dart';
import 'wings_socket.dart';
2021-06-20 12:37:20 +00:00
class WingsResponseContext extends ResponseContext<int?> {
2019-04-30 17:40:11 +00:00
@override
final Angel app;
2019-04-29 08:22:36 +00:00
@override
2021-06-20 12:37:20 +00:00
final WingsRequestContext? correspondingRequest;
2019-04-29 08:22:36 +00:00
2021-06-20 12:37:20 +00:00
LockableBytesBuilder? _buffer;
2018-07-03 23:16:29 +00:00
@override
2021-06-20 12:37:20 +00:00
final int? rawResponse;
2019-04-29 08:22:36 +00:00
bool _isDetached = false, _isClosed = false, _streamInitialized = false;
2019-04-30 17:40:11 +00:00
WingsResponseContext(this.app, this.rawResponse, [this.correspondingRequest]);
2019-04-29 08:22:36 +00:00
2021-06-20 12:37:20 +00:00
Iterable<String>? __allowedEncodings;
2019-04-29 08:22:36 +00:00
2021-06-20 12:37:20 +00:00
Iterable<String>? get _allowedEncodings {
return __allowedEncodings ??= correspondingRequest!.headers
2019-04-29 08:22:36 +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-04-29 08:22:36 +00:00
// Ignore quality specifications in accept-encoding
// ex. gzip;q=0.8
if (!str.contains(';')) return str;
return str.split(';')[0];
});
}
bool _openStream() {
if (!_streamInitialized) {
// If this is the first stream added to this response,
// then add headers, status code, etc.
var outHeaders = <String, String>{};
var statusLine =
utf8.encode('HTTP/1.1 $statusCode').followedBy([$cr, $lf]);
writeToNativeSocket(rawResponse, Uint8List.fromList(statusLine.toList()));
headers.forEach((k, v) => outHeaders[k] = v);
if (headers.containsKey('content-length')) {
2021-06-20 12:37:20 +00:00
var l = int.tryParse(headers['content-length']!);
2019-04-29 08:22:36 +00:00
if (l != null) {
outHeaders['content-length'] = l.toString();
}
}
2019-08-09 14:53:38 +00:00
if (contentType != null) {
2019-04-29 08:22:36 +00:00
outHeaders['content-type'] = contentType.toString();
2019-08-09 14:53:38 +00:00
}
2019-04-29 08:22:36 +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-04-29 08:22:36 +00:00
2019-08-09 14:53:38 +00:00
if (encoders.containsKey(encodingName)) {
2019-04-29 08:22:36 +00:00
encoder = encoders[encodingName];
2019-08-09 14:53:38 +00:00
} else if (encodingName == '*') {
2019-04-29 08:22:36 +00:00
encoder = encoders[key = encoders.keys.first];
}
if (encoder != null) {
outHeaders['content-encoding'] = key;
break;
}
}
}
}
void _wh(String k, String v) {
2019-04-30 17:40:11 +00:00
// var vv =Uri.encodeComponent(v);
var vv = v;
var headerLine = utf8.encode('$k: $vv').followedBy([$cr, $lf]);
2019-04-29 08:22:36 +00:00
writeToNativeSocket(
rawResponse, Uint8List.fromList(headerLine.toList()));
}
outHeaders.forEach(_wh);
for (var c in cookies) {
_wh('set-cookie', c.toString());
}
writeToNativeSocket(rawResponse, Uint8List.fromList([$cr, $lf]));
//_isClosed = true;
return _streamInitialized = true;
}
return false;
2018-07-03 23:16:29 +00:00
}
@override
Future addStream(Stream<List<int>> stream) {
2019-04-29 08:22:36 +00:00
if (_isClosed && isBuffered) throw ResponseContext.closed();
_openStream();
2021-06-20 12:37:20 +00:00
var output = stream;
2019-04-29 08:22:36 +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-04-29 08:22:36 +00:00
2019-08-09 14:53:38 +00:00
if (encoders.containsKey(encodingName)) {
2019-04-29 08:22:36 +00:00
encoder = encoders[encodingName];
2019-08-09 14:53:38 +00:00
} else if (encodingName == '*') {
2019-04-29 08:22:36 +00:00
encoder = encoders[key = encoders.keys.first];
}
if (encoder != null) {
2021-06-20 12:37:20 +00:00
output = encoders[key]!.bind(output);
2019-04-29 08:22:36 +00:00
break;
}
}
}
}
return output.forEach((buf) {
2019-08-09 14:53:38 +00:00
if (!_isClosed) {
2019-04-30 17:40:11 +00:00
writeToNativeSocket(
rawResponse, buf is Uint8List ? buf : Uint8List.fromList(buf));
2019-08-09 14:53:38 +00:00
}
2019-04-29 08:22:36 +00:00
});
2018-07-03 23:16:29 +00:00
}
@override
2019-04-29 08:22:36 +00:00
void add(List<int> data) {
2019-08-09 14:53:38 +00:00
if (_isClosed && isBuffered) {
2019-04-29 08:22:36 +00:00
throw ResponseContext.closed();
2019-08-09 14:53:38 +00:00
} else if (!isBuffered) {
2019-04-29 08:22:36 +00:00
if (!_isClosed) {
_openStream();
2018-07-03 23:16:29 +00:00
2019-04-29 08:22:36 +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-04-29 08:22:36 +00:00
2019-08-09 14:53:38 +00:00
if (encoders.containsKey(encodingName)) {
2019-04-29 08:22:36 +00:00
encoder = encoders[encodingName];
2019-08-09 14:53:38 +00:00
} else if (encodingName == '*') {
2019-04-29 08:22:36 +00:00
encoder = encoders[key = encoders.keys.first];
}
if (encoder != null) {
2021-06-20 12:37:20 +00:00
data = encoders[key]!.convert(data);
2019-04-29 08:22:36 +00:00
break;
}
}
}
}
writeToNativeSocket(
rawResponse, data is Uint8List ? data : Uint8List.fromList(data));
}
2019-08-09 14:53:38 +00:00
} else {
2021-06-20 12:37:20 +00:00
buffer!.add(data);
2019-08-09 14:53:38 +00:00
}
2019-04-29 08:22:36 +00:00
}
2018-07-03 23:16:29 +00:00
@override
2019-04-30 17:40:11 +00:00
Future close() async {
2019-04-29 08:22:36 +00:00
if (!_isDetached) {
if (!_isClosed) {
2019-04-30 17:40:11 +00:00
_isClosed = true;
2019-04-29 08:22:36 +00:00
if (!isBuffered) {
2019-04-30 17:40:11 +00:00
_openStream();
closeNativeSocketDescriptor(rawResponse);
2019-04-29 08:22:36 +00:00
} else {
2021-06-20 12:37:20 +00:00
_buffer!.lock();
2019-04-29 08:22:36 +00:00
}
}
2019-04-30 17:40:11 +00:00
await correspondingRequest?.close();
await super.close();
2019-04-29 08:22:36 +00:00
}
2018-07-03 23:16:29 +00:00
}
@override
2021-06-20 12:37:20 +00:00
BytesBuilder? get buffer => _buffer;
2018-07-03 23:16:29 +00:00
@override
2019-04-29 08:22:36 +00:00
int detach() {
_isDetached = true;
2021-06-20 12:37:20 +00:00
return rawResponse!;
2019-04-29 08:22:36 +00:00
}
@override
bool get isBuffered => _buffer != null;
2018-07-03 23:16:29 +00:00
@override
2019-04-29 08:22:36 +00:00
bool get isOpen => !_isClosed && !_isDetached;
2018-07-03 23:16:29 +00:00
@override
void useBuffer() {
2019-04-29 08:22:36 +00:00
_buffer = LockableBytesBuilder();
2018-07-03 23:16:29 +00:00
}
2019-04-29 08:22:36 +00:00
}