import 'dart:async'; import 'dart:convert'; import 'dart:io' hide BytesBuilder; import 'dart:typed_data'; import 'package:charcode/ascii.dart'; import 'connection_info.dart'; import 'lockable_headers.dart'; class MockHttpResponse extends Stream> implements HttpResponse { BytesBuilder _buf = BytesBuilder(); bool _bufferOutput = true; final Completer _done = Completer(); final LockableMockHttpHeaders _headers = LockableMockHttpHeaders(); final StreamController> _stream = StreamController>(); @override final List cookies = []; @override HttpConnectionInfo connectionInfo = MockHttpConnectionInfo(remoteAddress: InternetAddress.anyIPv4); /// [copyBuffer] corresponds to `copy` on the [BytesBuilder] constructor. MockHttpResponse( {bool copyBuffer = true, required this.statusCode, required this.reasonPhrase, required this.contentLength, this.deadline, required this.encoding, required this.persistentConnection, bool? bufferOutput}) { _buf = BytesBuilder(copy: copyBuffer != false); _bufferOutput = bufferOutput != false; statusCode = 200; } @override bool get bufferOutput => _bufferOutput; @override set bufferOutput(bool value) {} @override int contentLength; @override Duration? deadline; @override bool persistentConnection; @override String reasonPhrase; @override int statusCode; @override Encoding encoding; @override HttpHeaders get headers => _headers; @override Future get done => _done.future; @override void add(List data) { if (_done.isCompleted) { throw StateError('Cannot add to closed MockHttpResponse.'); } else { _headers.lock(); if (_bufferOutput == true) { _buf.add(data); } else { _stream.add(data); } } } @override void addError(error, [StackTrace? stackTrace]) { if (_done.isCompleted) { throw StateError('Cannot add to closed MockHttpResponse.'); } else { _stream.addError(error, stackTrace); } } @override Future addStream(Stream> stream) { var c = Completer(); stream.listen(add, onError: addError, onDone: c.complete); return c.future; } @override Future close() async { _headers.lock(); await flush(); scheduleMicrotask(_stream.close); _done.complete(); //return await _done.future; } @override Future detachSocket({bool writeHeaders = true}) { throw UnsupportedError('MockHttpResponses have no socket to detach.'); } @override Future flush() async { _stream.add(_buf.takeBytes()); } @override Future redirect(Uri location, {int status = HttpStatus.movedTemporarily}) async { statusCode = status; } @override void write(Object? obj) { obj?.toString().codeUnits.forEach(writeCharCode); } @override void writeAll(Iterable objects, [String separator = '']) { write(objects.join(separator)); } @override void writeCharCode(int charCode) { add([charCode]); } @override void writeln([Object? obj = '']) { write(obj ?? ''); add([$cr, $lf]); } @override StreamSubscription> listen(void Function(List event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) => _stream.stream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError == true); }