This repository has been archived by the owner on Aug 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 42
/
http_mock.dart
276 lines (225 loc) · 6.69 KB
/
http_mock.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
library redstone.src.http_mock;
//copied from http_server/test/http_mock.dart
import 'dart:io';
import 'dart:collection';
import 'dart:async';
import 'dart:convert';
class MockHttpHeaders implements HttpHeaders {
final Map<String, List<String>> _headers =
new HashMap<String, List<String>>();
ContentType _contentType;
bool chunkedTransferEncoding;
MockHttpHeaders([Map<String, List<String>> values]) {
if (values != null) {
_headers.addAll(values);
}
}
List<String> operator [](String name) => _headers[name.toLowerCase()];
DateTime get ifModifiedSince {
List<String> values = _headers[HttpHeaders.IF_MODIFIED_SINCE];
if (values != null) {
try {
return HttpDate.parse(values[0]);
} on Exception {
return null;
}
}
return null;
}
void set ifModifiedSince(DateTime ifModifiedSince) {
// Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT).
String formatted = HttpDate.format(ifModifiedSince.toUtc());
_set(HttpHeaders.IF_MODIFIED_SINCE, formatted);
}
DateTime get date {
List<String> values = _headers[HttpHeaders.DATE];
if (values != null) {
try {
return HttpDate.parse(values[0]);
} on Exception {
return null;
}
}
return null;
}
void set date(DateTime date) {
// Format "DateTime" header with date in Greenwich Mean Time (GMT).
String formatted = HttpDate.format(date.toUtc());
_set("date", formatted);
}
void set contentType(ContentType type) {
if (_contentType == null && _headers["content-type"] == null) {
_contentType = type;
set("content-type", type.value);
}
}
ContentType get contentType {
if (_contentType != null) {
return _contentType;
}
var ct = value("content-type");
if (ct != null) {
return ContentType.parse(ct);
}
return null;
}
void set(String name, Object value) {
name = name.toLowerCase();
_headers.remove(name);
_addAll(name, value);
}
void add(String name, Object value) {
name = name.toLowerCase();
_addAll(name, value);
}
String value(String name) {
name = name.toLowerCase();
List<String> values = _headers[name];
if (values == null) return null;
if (values.length > 1) {
throw new HttpException("More than one value for header $name");
}
return values.first;
}
void forEach(void f(String name, List<String> values)) => _headers.forEach(f);
String toString() => '$runtimeType : $_headers';
// [name] must be a lower-case version of the name.
void _add(String name, value) {
if (name == HttpHeaders.IF_MODIFIED_SINCE) {
if (value is DateTime) {
ifModifiedSince = value;
} else if (value is String) {
_set(HttpHeaders.IF_MODIFIED_SINCE, value);
} else {
throw new HttpException("Unexpected type for header named $name");
}
} else {
_addValue(name, value);
}
}
void _addAll(String name, value) {
if (value is List) {
for (int i = 0; i < value.length; i++) {
_add(name, value[i]);
}
} else {
_add(name, value);
}
}
void _addValue(String name, Object value) {
List<String> values = _headers[name];
if (values == null) {
values = new List<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());
List<String> values = new List<String>();
_headers[name] = values;
values.add(value);
}
dynamic noSuchMethod(Invocation invocation) {
return super.noSuchMethod(invocation);
}
}
class MockHttpResponse implements HttpResponse {
final HttpHeaders headers = new MockHttpHeaders();
final Completer _completer = new Completer();
final List<int> _buffer = new List<int>();
String _reasonPhrase;
Future _doneFuture;
MockHttpResponse() {
_doneFuture = _completer.future.whenComplete(() {
assert(!_isDone);
_isDone = true;
});
}
bool _isDone = false;
int statusCode = HttpStatus.OK;
String get reasonPhrase => _findReasonPhrase(statusCode);
void set reasonPhrase(String value) {
_reasonPhrase = value;
}
Future get done => _doneFuture;
Future close() {
_completer.complete();
return _doneFuture;
}
void add(List<int> data) {
_buffer.addAll(data);
}
Future addStream(Stream<List<int>> stream) {
var completer = new Completer();
stream.listen((data) {
_buffer.addAll(data);
}, onDone: () => completer.complete());
return completer.future;
}
void addError(dynamic error, [StackTrace stackTrace]) {
// doesn't seem to be hit...hmm...
}
Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY}) {
this.statusCode = status;
headers.set(HttpHeaders.LOCATION, location.toString());
return close();
}
void write(Object obj) {
var str = obj.toString();
add(UTF8.encode(str));
}
Future<Socket> detachSocket({bool writeHeaders: true}) {
throw "MockHttpResponse.detachSocket: Unsupported Operation";
}
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
String get mockContent => UTF8.decode(_buffer);
bool get mockDone => _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.NOT_FOUND:
return "Not Found";
default:
return "Status $statusCode";
}
}
}
class MockHttpRequest extends Stream<List<int>> implements HttpRequest {
final Uri requestedUri;
final Uri uri;
final MockHttpResponse response = new MockHttpResponse();
final HttpHeaders headers;
final String method;
final bool followRedirects;
final HttpSession session;
final Stream<List<int>> body;
MockHttpRequest(
this.requestedUri, this.uri, this.method, this.headers, this.body,
{this.session, this.followRedirects: true, DateTime ifModifiedSince}) {
if (ifModifiedSince != null) {
headers.ifModifiedSince = ifModifiedSince;
}
}
@override
StreamSubscription<List<int>> listen(void onData(List<int> event),
{Function onError, void onDone(), bool cancelOnError}) {
return body.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
int get contentLength => -1;
@override
String get protocolVersion => "1.1";
dynamic noSuchMethod(Invocation invocation) {
return super.noSuchMethod(invocation);
}
}