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