Showing
2 changed files
with
421 additions
and
0 deletions
1 | +import 'dart:async'; | ||
2 | +import 'dart:convert'; | ||
3 | +import 'dart:io'; | ||
4 | + | ||
5 | +import 'package:auto_track/auto_track/track/track.dart'; | ||
6 | + | ||
7 | +import '../../utils/request_model.dart'; | ||
8 | +import '../page_view/page_stack.dart'; | ||
9 | + | ||
10 | +class HttpClientRequestWithChecker implements HttpClientRequest { | ||
11 | + final HttpClientRequest _realRequest; | ||
12 | + final Stopwatch _stopwatch; | ||
13 | + final Page? pageInfoData; | ||
14 | + | ||
15 | + HttpClientRequestWithChecker( | ||
16 | + this._realRequest, this._stopwatch, this.pageInfoData); | ||
17 | + | ||
18 | + @override | ||
19 | + bool get bufferOutput => _realRequest.bufferOutput; | ||
20 | + | ||
21 | + @override | ||
22 | + int get contentLength => _realRequest.contentLength; | ||
23 | + | ||
24 | + @override | ||
25 | + Encoding get encoding => _realRequest.encoding; | ||
26 | + | ||
27 | + @override | ||
28 | + bool get followRedirects => _realRequest.followRedirects; | ||
29 | + | ||
30 | + @override | ||
31 | + int get maxRedirects => _realRequest.maxRedirects; | ||
32 | + | ||
33 | + @override | ||
34 | + bool get persistentConnection => _realRequest.persistentConnection; | ||
35 | + | ||
36 | + @override | ||
37 | + void add(List<int> data) { | ||
38 | + _realRequest.add(data); | ||
39 | + } | ||
40 | + | ||
41 | + @override | ||
42 | + void addError(Object error, [StackTrace? stackTrace]) { | ||
43 | + _realRequest.addError(error, stackTrace); | ||
44 | + } | ||
45 | + | ||
46 | + @override | ||
47 | + Future addStream(Stream<List<int>> stream) { | ||
48 | + return _realRequest.addStream(stream); | ||
49 | + } | ||
50 | + | ||
51 | + @override | ||
52 | + Future<HttpClientResponse> close() async { | ||
53 | + return _realRequest.close().then((HttpClientResponse response) { | ||
54 | + _checkResponse(_realRequest, response); | ||
55 | + return response; | ||
56 | + }).catchError((dynamic error, dynamic stackTrace) {}, test: (error) { | ||
57 | + _stopwatch.stop(); | ||
58 | + String message; | ||
59 | + if (error is HttpException) { | ||
60 | + message = error.message; | ||
61 | + } else { | ||
62 | + message = error.toString(); | ||
63 | + } | ||
64 | + Track.instance.reportHttpRequest(RequestModel( | ||
65 | + uri: _realRequest.uri, | ||
66 | + method: method, | ||
67 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
68 | + requestHeaders: _realRequest.headers, | ||
69 | + message: message, | ||
70 | + status: -1, | ||
71 | + spent: _stopwatch.elapsedMilliseconds)); | ||
72 | + return false; | ||
73 | + }); | ||
74 | + } | ||
75 | + | ||
76 | + @override | ||
77 | + HttpConnectionInfo? get connectionInfo => _realRequest.connectionInfo; | ||
78 | + | ||
79 | + @override | ||
80 | + List<Cookie> get cookies => _realRequest.cookies; | ||
81 | + | ||
82 | + @override | ||
83 | + Future<HttpClientResponse> get done async { | ||
84 | + return close(); | ||
85 | + } | ||
86 | + | ||
87 | + @override | ||
88 | + Future flush() { | ||
89 | + return _realRequest.flush(); | ||
90 | + } | ||
91 | + | ||
92 | + @override | ||
93 | + HttpHeaders get headers => _realRequest.headers; | ||
94 | + | ||
95 | + @override | ||
96 | + String get method => _realRequest.method; | ||
97 | + | ||
98 | + @override | ||
99 | + Uri get uri => _realRequest.uri; | ||
100 | + | ||
101 | + @override | ||
102 | + void write(Object? obj) { | ||
103 | + _realRequest.write(obj); | ||
104 | + } | ||
105 | + | ||
106 | + @override | ||
107 | + void writeAll(Iterable objects, [String separator = '']) { | ||
108 | + _realRequest.writeAll(objects, separator); | ||
109 | + } | ||
110 | + | ||
111 | + @override | ||
112 | + void writeCharCode(int charCode) { | ||
113 | + _realRequest.writeCharCode(charCode); | ||
114 | + } | ||
115 | + | ||
116 | + @override | ||
117 | + void writeln([Object? obj = '']) { | ||
118 | + _realRequest.writeln(obj); | ||
119 | + } | ||
120 | + | ||
121 | + void _checkResponse(HttpClientRequest request, HttpClientResponse response) { | ||
122 | + String message = 'status ${response.statusCode}'; | ||
123 | + message = '$message: ${response.reasonPhrase}'; | ||
124 | + | ||
125 | + _stopwatch.stop(); | ||
126 | + | ||
127 | + Track.instance.reportHttpRequest(RequestModel( | ||
128 | + uri: _realRequest.uri, | ||
129 | + method: method, | ||
130 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
131 | + requestHeaders: request.headers, | ||
132 | + responseHeaders: response.headers, | ||
133 | + message: message, | ||
134 | + status: response.statusCode, | ||
135 | + spent: _stopwatch.elapsedMilliseconds)); | ||
136 | + } | ||
137 | + | ||
138 | + @override | ||
139 | + set bufferOutput(bool bufferOutput) { | ||
140 | + _realRequest.bufferOutput = bufferOutput; | ||
141 | + } | ||
142 | + | ||
143 | + @override | ||
144 | + set contentLength(int contentLength) { | ||
145 | + _realRequest.contentLength = contentLength; | ||
146 | + } | ||
147 | + | ||
148 | + @override | ||
149 | + set encoding(Encoding encoding) { | ||
150 | + _realRequest.encoding = encoding; | ||
151 | + } | ||
152 | + | ||
153 | + @override | ||
154 | + set followRedirects(bool followRedirects) { | ||
155 | + _realRequest.followRedirects = followRedirects; | ||
156 | + } | ||
157 | + | ||
158 | + @override | ||
159 | + set maxRedirects(int maxRedirects) { | ||
160 | + _realRequest.maxRedirects = maxRedirects; | ||
161 | + } | ||
162 | + | ||
163 | + @override | ||
164 | + set persistentConnection(bool persistentConnection) { | ||
165 | + _realRequest.persistentConnection = persistentConnection; | ||
166 | + } | ||
167 | + | ||
168 | + @override | ||
169 | + void abort([Object? exception, StackTrace? stackTrace]) { | ||
170 | + _realRequest.abort(exception, stackTrace); | ||
171 | + } | ||
172 | +} | ||
173 | + | ||
174 | +class HttpClientWithChecker implements HttpClient { | ||
175 | + final HttpClient _realClient; | ||
176 | + | ||
177 | + Uri? url; | ||
178 | + String? method; | ||
179 | + | ||
180 | + HttpClientWithChecker(this._realClient); | ||
181 | + | ||
182 | + @override | ||
183 | + set connectionFactory( | ||
184 | + Future<ConnectionTask<Socket>> Function( | ||
185 | + Uri url, String? proxyHost, int? proxyPort)? | ||
186 | + f) { | ||
187 | + // TODO: add impl here | ||
188 | + assert(false); | ||
189 | + } | ||
190 | + | ||
191 | + @override | ||
192 | + set keyLog(Function(String line)? callback) { | ||
193 | + // TODO: add impl here | ||
194 | + assert(false); | ||
195 | + } | ||
196 | + | ||
197 | + @override | ||
198 | + bool get autoUncompress => _realClient.autoUncompress; | ||
199 | + | ||
200 | + @override | ||
201 | + set autoUncompress(bool value) => _realClient.autoUncompress = value; | ||
202 | + | ||
203 | + @override | ||
204 | + Duration? get connectionTimeout => _realClient.connectionTimeout; | ||
205 | + | ||
206 | + @override | ||
207 | + set connectionTimeout(Duration? value) => | ||
208 | + _realClient.connectionTimeout = value; | ||
209 | + | ||
210 | + @override | ||
211 | + Duration get idleTimeout => _realClient.idleTimeout; | ||
212 | + | ||
213 | + @override | ||
214 | + set idleTimeout(Duration value) => _realClient.idleTimeout = value; | ||
215 | + | ||
216 | + @override | ||
217 | + int? get maxConnectionsPerHost => _realClient.maxConnectionsPerHost; | ||
218 | + | ||
219 | + @override | ||
220 | + set maxConnectionsPerHost(int? value) => | ||
221 | + _realClient.maxConnectionsPerHost = value; | ||
222 | + | ||
223 | + @override | ||
224 | + String? get userAgent => _realClient.userAgent; | ||
225 | + | ||
226 | + @override | ||
227 | + set userAgent(String? value) => _realClient.userAgent = value; | ||
228 | + | ||
229 | + @override | ||
230 | + void addCredentials( | ||
231 | + Uri url, String realm, HttpClientCredentials credentials) => | ||
232 | + _realClient.addCredentials(url, realm, credentials); | ||
233 | + | ||
234 | + @override | ||
235 | + void addProxyCredentials(String host, int port, String realm, | ||
236 | + HttpClientCredentials credentials) => | ||
237 | + _realClient.addProxyCredentials(host, port, realm, credentials); | ||
238 | + | ||
239 | + @override | ||
240 | + set authenticate( | ||
241 | + Future<bool> Function(Uri url, String scheme, String? realm)? f) => | ||
242 | + _realClient.authenticate = f; | ||
243 | + | ||
244 | + @override | ||
245 | + set authenticateProxy( | ||
246 | + Future<bool> Function( | ||
247 | + String host, int port, String scheme, String? realm)? | ||
248 | + f) => | ||
249 | + _realClient.authenticateProxy = f; | ||
250 | + | ||
251 | + @override | ||
252 | + set badCertificateCallback( | ||
253 | + bool Function(X509Certificate cert, String host, int port)? | ||
254 | + callback) => | ||
255 | + _realClient.badCertificateCallback = callback; | ||
256 | + | ||
257 | + @override | ||
258 | + void close({bool force = false}) => _realClient.close(force: force); | ||
259 | + | ||
260 | + @override | ||
261 | + Future<HttpClientRequest> delete(String host, int port, String path) => | ||
262 | + _realClient.delete(host, port, path); | ||
263 | + | ||
264 | + @override | ||
265 | + Future<HttpClientRequest> deleteUrl(Uri url) => _realClient.deleteUrl(url); | ||
266 | + | ||
267 | + @override | ||
268 | + set findProxy(String Function(Uri url)? f) => _realClient.findProxy = f; | ||
269 | + | ||
270 | + @override | ||
271 | + Future<HttpClientRequest> get(String host, int port, String path) => | ||
272 | + _realClient.get(host, port, path); | ||
273 | + | ||
274 | + @override | ||
275 | + Future<HttpClientRequest> getUrl(Uri url) => | ||
276 | + _addCheck(_realClient.getUrl(url), 'get', url); | ||
277 | + | ||
278 | + @override | ||
279 | + Future<HttpClientRequest> head(String host, int port, String path) => | ||
280 | + _realClient.head(host, port, path); | ||
281 | + | ||
282 | + @override | ||
283 | + Future<HttpClientRequest> headUrl(Uri url) => | ||
284 | + _addCheck(_realClient.headUrl(url), 'head', url); | ||
285 | + | ||
286 | + @override | ||
287 | + Future<HttpClientRequest> patch(String host, int port, String path) => | ||
288 | + _realClient.patch(host, port, path); | ||
289 | + | ||
290 | + @override | ||
291 | + Future<HttpClientRequest> patchUrl(Uri url) => | ||
292 | + _addCheck(_realClient.patchUrl(url), 'patch', url); | ||
293 | + | ||
294 | + @override | ||
295 | + Future<HttpClientRequest> post(String host, int port, String path) => | ||
296 | + _realClient.post(host, port, path); | ||
297 | + | ||
298 | + @override | ||
299 | + Future<HttpClientRequest> postUrl(Uri url) => | ||
300 | + _addCheck(_realClient.postUrl(url), 'post', url); | ||
301 | + | ||
302 | + @override | ||
303 | + Future<HttpClientRequest> put(String host, int port, String path) => | ||
304 | + _realClient.put(host, port, path); | ||
305 | + | ||
306 | + @override | ||
307 | + Future<HttpClientRequest> putUrl(Uri url) => | ||
308 | + _addCheck(_realClient.putUrl(url), 'put', url); | ||
309 | + | ||
310 | + @override | ||
311 | + Future<HttpClientRequest> open( | ||
312 | + String method, String host, int port, String path) { | ||
313 | + const int hashMark = 0x23; | ||
314 | + const int questionMark = 0x3f; | ||
315 | + int fragmentStart = path.length; | ||
316 | + int queryStart = path.length; | ||
317 | + for (int i = path.length - 1; i >= 0; i--) { | ||
318 | + final char = path.codeUnitAt(i); | ||
319 | + if (char == hashMark) { | ||
320 | + fragmentStart = i; | ||
321 | + queryStart = i; | ||
322 | + } else if (char == questionMark) { | ||
323 | + queryStart = i; | ||
324 | + } | ||
325 | + } | ||
326 | + String? query; | ||
327 | + if (queryStart < fragmentStart) { | ||
328 | + query = path.substring(queryStart + 1, fragmentStart); | ||
329 | + path = path.substring(0, queryStart); | ||
330 | + } | ||
331 | + final Uri uri = | ||
332 | + Uri(scheme: 'http', host: host, port: port, path: path, query: query); | ||
333 | + return _addCheck(_realClient.open(method, host, port, path), method, uri); | ||
334 | + } | ||
335 | + | ||
336 | + @override | ||
337 | + Future<HttpClientRequest> openUrl(String method, Uri url) => | ||
338 | + _addCheck(_realClient.openUrl(method, url), method, url); | ||
339 | + | ||
340 | + Future<HttpClientRequest> _addCheck( | ||
341 | + Future<HttpClientRequest> request, String method, Uri url) { | ||
342 | + final Stopwatch stopwatch = Stopwatch()..start(); | ||
343 | + final Page? pageInfoData = PageStack.instance.getCurrentPage(); | ||
344 | + return request | ||
345 | + .then((HttpClientRequest request) => | ||
346 | + HttpClientRequestWithChecker(request, stopwatch, pageInfoData)) | ||
347 | + .catchError((dynamic error, dynamic stackTrace) {}, test: (error) { | ||
348 | + String message = error.toString(); | ||
349 | + if (error is SocketException) { | ||
350 | + message = error.message; | ||
351 | + } | ||
352 | + Track.instance.reportHttpRequest(RequestModel( | ||
353 | + uri: url, | ||
354 | + method: method, | ||
355 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
356 | + requestHeaders: null, | ||
357 | + message: message, | ||
358 | + status: -1, | ||
359 | + spent: stopwatch.elapsedMilliseconds)); | ||
360 | + return false; | ||
361 | + }); | ||
362 | + } | ||
363 | +} | ||
364 | + | ||
365 | +class _DefaultHttpOverrides extends HttpOverrides {} | ||
366 | + | ||
367 | +class AutoTrackHttpOverrides extends HttpOverrides { | ||
368 | + HttpOverrides? _currentOverrides; | ||
369 | + | ||
370 | + AutoTrackHttpOverrides(this._currentOverrides) : super() { | ||
371 | + _currentOverrides ??= _DefaultHttpOverrides(); | ||
372 | + } | ||
373 | + | ||
374 | + @override | ||
375 | + HttpClient createHttpClient(SecurityContext? context) { | ||
376 | + return HttpClientWithChecker(_currentOverrides!.createHttpClient(context)); | ||
377 | + } | ||
378 | + | ||
379 | + @override | ||
380 | + String findProxyFromEnvironment(Uri url, Map<String, String>? environment) { | ||
381 | + return _currentOverrides!.findProxyFromEnvironment(url, environment); | ||
382 | + } | ||
383 | +} | ||
384 | + |
lib/auto_track/utils/request_model.dart
0 → 100644
1 | +class RequestModel { | ||
2 | + RequestModel({ | ||
3 | + required this.uri, | ||
4 | + required this.method, | ||
5 | + required this.message, | ||
6 | + required this.status, | ||
7 | + required this.spent, | ||
8 | + required this.pageId, | ||
9 | + this.requestBody, | ||
10 | + this.requestHeaders, | ||
11 | + this.responseHeaders, | ||
12 | + }); | ||
13 | + | ||
14 | + Uri uri; | ||
15 | + String method; | ||
16 | + String message; | ||
17 | + String pageId; | ||
18 | + int status; | ||
19 | + int spent; | ||
20 | + dynamic requestBody; | ||
21 | + dynamic requestHeaders; | ||
22 | + dynamic responseHeaders; | ||
23 | + | ||
24 | + Map<String, dynamic> toMap() { | ||
25 | + return { | ||
26 | + 'uri': uri, | ||
27 | + 'method': method, | ||
28 | + 'message': message, | ||
29 | + 'pageId': pageId, | ||
30 | + 'status': status, | ||
31 | + 'spent': spent, | ||
32 | + 'requestBody': requestBody, | ||
33 | + 'requestHeaders': requestHeaders, | ||
34 | + 'responseHeaders': responseHeaders, | ||
35 | + }; | ||
36 | + } | ||
37 | +} |
-
Please register or login to post a comment