240 lines
5.6 KiB
Dart
240 lines
5.6 KiB
Dart
import 'dart:async';
|
|
import 'dart:collection';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
class FakeHttpHeaders implements HttpHeaders {
|
|
final Map<String, List<String>> _headers = HashMap<String, List<String>>();
|
|
|
|
@override
|
|
List<String>? operator [](key) => _headers[key];
|
|
|
|
@override
|
|
int get contentLength =>
|
|
int.parse(_headers[HttpHeaders.contentLengthHeader]![0]);
|
|
|
|
@override
|
|
DateTime? get ifModifiedSince {
|
|
var values = _headers[HttpHeaders.ifModifiedSinceHeader];
|
|
if (values != null) {
|
|
try {
|
|
return HttpDate.parse(values[0]);
|
|
} on Exception {
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
set ifModifiedSince(DateTime? ifModifiedSince) {
|
|
ArgumentError.checkNotNull(ifModifiedSince);
|
|
// Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT).
|
|
var formatted = HttpDate.format(ifModifiedSince!.toUtc());
|
|
_set(HttpHeaders.ifModifiedSinceHeader, formatted);
|
|
}
|
|
|
|
@override
|
|
ContentType? contentType;
|
|
|
|
@override
|
|
void set(String name, Object value, {bool preserveHeaderCase = false}) {
|
|
if (preserveHeaderCase) {
|
|
throw ArgumentError('preserveHeaderCase not supported');
|
|
}
|
|
name = name.toLowerCase();
|
|
_headers.remove(name);
|
|
_addAll(name, value);
|
|
}
|
|
|
|
@override
|
|
String? value(String name) {
|
|
name = name.toLowerCase();
|
|
var values = _headers[name];
|
|
if (values == null) return null;
|
|
if (values.length > 1) {
|
|
throw HttpException('More than one value for header $name');
|
|
}
|
|
return values[0];
|
|
}
|
|
|
|
@override
|
|
String toString() => '$runtimeType : $_headers';
|
|
|
|
// [name] must be a lower-case version of the name.
|
|
void _add(String name, Object value) {
|
|
if (name == HttpHeaders.ifModifiedSinceHeader) {
|
|
if (value is DateTime) {
|
|
ifModifiedSince = value;
|
|
} else if (value is String) {
|
|
_set(HttpHeaders.ifModifiedSinceHeader, value);
|
|
} else {
|
|
throw HttpException('Unexpected type for header named $name');
|
|
}
|
|
} else {
|
|
_addValue(name, value);
|
|
}
|
|
}
|
|
|
|
void _addAll(String name, Object value) {
|
|
if (value is List) {
|
|
for (var i = 0; i < value.length; i++) {
|
|
_add(name, value[i] as Object);
|
|
}
|
|
} else {
|
|
_add(name, value);
|
|
}
|
|
}
|
|
|
|
void _addValue(String name, Object value) {
|
|
var values = _headers[name];
|
|
if (values == null) {
|
|
values = <String>[];
|
|
_headers[name] = values;
|
|
}
|
|
if (value is DateTime) {
|
|
values.add(HttpDate.format(value));
|
|
} else {
|
|
values.add(value.toString());
|
|
}
|
|
}
|
|
|
|
void _set(String name, String value) {
|
|
assert(name == name.toLowerCase());
|
|
var values = <String>[];
|
|
_headers[name] = values;
|
|
values.add(value);
|
|
}
|
|
|
|
/*
|
|
* Implemented to remove editor warnings
|
|
*/
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) {
|
|
print([
|
|
invocation.memberName,
|
|
invocation.isGetter,
|
|
invocation.isSetter,
|
|
invocation.isMethod,
|
|
invocation.isAccessor
|
|
]);
|
|
return super.noSuchMethod(invocation);
|
|
}
|
|
}
|
|
|
|
class FakeHttpRequest extends StreamView<Uint8List> implements HttpRequest {
|
|
@override
|
|
final Uri uri;
|
|
@override
|
|
final FakeHttpResponse response = FakeHttpResponse();
|
|
@override
|
|
final HttpHeaders headers = FakeHttpHeaders();
|
|
@override
|
|
final String method = 'GET';
|
|
final bool followRedirects;
|
|
|
|
FakeHttpRequest(this.uri,
|
|
{this.followRedirects = true,
|
|
DateTime? ifModifiedSince,
|
|
required Stream<Uint8List> data})
|
|
: super(data) {
|
|
if (ifModifiedSince != null) {
|
|
headers.ifModifiedSince = ifModifiedSince;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Implemented to remove editor warnings
|
|
*/
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
}
|
|
|
|
class FakeHttpResponse implements HttpResponse {
|
|
@override
|
|
final HttpHeaders headers = FakeHttpHeaders();
|
|
final Completer _completer = Completer();
|
|
final List<int> _buffer = <int>[];
|
|
String? _reasonPhrase;
|
|
late final Future _doneFuture;
|
|
|
|
FakeHttpResponse() {
|
|
_doneFuture = _completer.future.whenComplete(() {
|
|
assert(!_isDone);
|
|
_isDone = true;
|
|
});
|
|
}
|
|
|
|
bool _isDone = false;
|
|
|
|
@override
|
|
int statusCode = HttpStatus.ok;
|
|
|
|
@override
|
|
String get reasonPhrase => _findReasonPhrase(statusCode)!;
|
|
|
|
@override
|
|
set reasonPhrase(String value) {
|
|
_reasonPhrase = value;
|
|
}
|
|
|
|
@override
|
|
Future get done => _doneFuture;
|
|
|
|
@override
|
|
Future close() {
|
|
_completer.complete();
|
|
return _doneFuture;
|
|
}
|
|
|
|
@override
|
|
void add(List<int> data) {
|
|
_buffer.addAll(data);
|
|
}
|
|
|
|
@override
|
|
void addError(error, [StackTrace? stackTrace]) {
|
|
// doesn't seem to be hit...hmm...
|
|
}
|
|
|
|
@override
|
|
Future redirect(Uri location, {int status = HttpStatus.movedTemporarily}) {
|
|
statusCode = status;
|
|
headers.set(HttpHeaders.locationHeader, location.toString());
|
|
return close();
|
|
}
|
|
|
|
@override
|
|
void write(Object? obj) {
|
|
var str = obj.toString();
|
|
add(utf8.encode(str));
|
|
}
|
|
|
|
/*
|
|
* Implemented to remove editor warnings
|
|
*/
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
|
|
String get fakeContent => utf8.decode(_buffer);
|
|
|
|
List<int> get fakeContentBinary => _buffer;
|
|
|
|
bool get fakeDone => _isDone;
|
|
|
|
// Copied from SDK http_impl.dart @ 845 on 2014-01-05
|
|
// TODO: file an SDK bug to expose this on HttpStatus in some way
|
|
String? _findReasonPhrase(int statusCode) {
|
|
if (_reasonPhrase != null) {
|
|
return _reasonPhrase;
|
|
}
|
|
|
|
switch (statusCode) {
|
|
case HttpStatus.notFound:
|
|
return 'Not Found';
|
|
default:
|
|
return 'Status $statusCode';
|
|
}
|
|
}
|
|
}
|