LCOV - code coverage report
Current view: top level - Users/duwen/Documents/code/dio/dio/lib/src - dio_mixin.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 189 228 82.9 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : import 'dart:collection';
       3             : import 'dart:convert';
       4             : import 'dart:math' as math;
       5             : import 'dart:typed_data';
       6             : 
       7             : import 'adapter.dart';
       8             : import 'cancel_token.dart';
       9             : import 'dio.dart';
      10             : import 'dio_error.dart';
      11             : import 'form_data.dart';
      12             : import 'headers.dart';
      13             : import 'options.dart';
      14             : import 'response.dart';
      15             : import 'transformer.dart';
      16             : 
      17             : import 'progress_stream_stub.dart'
      18             : // ignore: uri_does_not_exist
      19             :     if (dart.library.html) 'progress_stream/browser_progress_stream.dart'
      20             : // ignore: uri_does_not_exist
      21             :     if (dart.library.io) 'progress_stream/io_progress_stream.dart';
      22             : 
      23             : part 'interceptor.dart';
      24             : 
      25             : abstract class DioMixin implements Dio {
      26             :   /// Default Request config. More see [BaseOptions].
      27             :   @override
      28             :   late BaseOptions options;
      29             : 
      30             :   /// Each Dio instance has a interceptor by which you can intercept requests or responses before they are
      31             :   /// handled by `then` or `catchError`. the [interceptor] field
      32             :   /// contains a [RequestInterceptor] and a [ResponseInterceptor] instance.
      33             :   final Interceptors _interceptors = Interceptors();
      34             : 
      35           8 :   @override
      36           8 :   Interceptors get interceptors => _interceptors;
      37             : 
      38             :   @override
      39             :   late HttpClientAdapter httpClientAdapter;
      40             : 
      41             :   @override
      42             :   Transformer transformer = DefaultTransformer();
      43             : 
      44             :   bool _closed = false;
      45             : 
      46           0 :   @override
      47             :   void close({bool force = false}) {
      48           0 :     _closed = true;
      49           0 :     httpClientAdapter.close(force: force);
      50             :   }
      51             : 
      52             :   /// Handy method to make http GET request, which is a alias of  [BaseDio.requestOptions].
      53           6 :   @override
      54             :   Future<Response<T>> get<T>(
      55             :     String path, {
      56             :     Map<String, dynamic>? queryParameters,
      57             :     Options? options,
      58             :     CancelToken? cancelToken,
      59             :     ProgressCallback? onReceiveProgress,
      60             :   }) {
      61           6 :     return request<T>(
      62             :       path,
      63             :       queryParameters: queryParameters,
      64           6 :       options: checkOptions('GET', options),
      65             :       onReceiveProgress: onReceiveProgress,
      66             :       cancelToken: cancelToken,
      67             :     );
      68             :   }
      69             : 
      70             :   /// Handy method to make http GET request, which is a alias of [BaseDio.requestOptions].
      71           1 :   @override
      72             :   Future<Response<T>> getUri<T>(
      73             :     Uri uri, {
      74             :     Options? options,
      75             :     CancelToken? cancelToken,
      76             :     ProgressCallback? onReceiveProgress,
      77             :   }) {
      78           1 :     return requestUri<T>(
      79             :       uri,
      80           1 :       options: checkOptions('GET', options),
      81             :       onReceiveProgress: onReceiveProgress,
      82             :       cancelToken: cancelToken,
      83             :     );
      84             :   }
      85             : 
      86             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.requestOptions].
      87           3 :   @override
      88             :   Future<Response<T>> post<T>(
      89             :     String path, {
      90             :     data,
      91             :     Map<String, dynamic>? queryParameters,
      92             :     Options? options,
      93             :     CancelToken? cancelToken,
      94             :     ProgressCallback? onSendProgress,
      95             :     ProgressCallback? onReceiveProgress,
      96             :   }) {
      97           3 :     return request<T>(
      98             :       path,
      99             :       data: data,
     100           3 :       options: checkOptions('POST', options),
     101             :       queryParameters: queryParameters,
     102             :       cancelToken: cancelToken,
     103             :       onSendProgress: onSendProgress,
     104             :       onReceiveProgress: onReceiveProgress,
     105             :     );
     106             :   }
     107             : 
     108             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.requestOptions].
     109           1 :   @override
     110             :   Future<Response<T>> postUri<T>(
     111             :     Uri uri, {
     112             :     data,
     113             :     Options? options,
     114             :     CancelToken? cancelToken,
     115             :     ProgressCallback? onSendProgress,
     116             :     ProgressCallback? onReceiveProgress,
     117             :   }) {
     118           1 :     return requestUri<T>(
     119             :       uri,
     120             :       data: data,
     121           1 :       options: checkOptions('POST', options),
     122             :       cancelToken: cancelToken,
     123             :       onSendProgress: onSendProgress,
     124             :       onReceiveProgress: onReceiveProgress,
     125             :     );
     126             :   }
     127             : 
     128             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.requestOptions].
     129           2 :   @override
     130             :   Future<Response<T>> put<T>(
     131             :     String path, {
     132             :     data,
     133             :     Map<String, dynamic>? queryParameters,
     134             :     Options? options,
     135             :     CancelToken? cancelToken,
     136             :     ProgressCallback? onSendProgress,
     137             :     ProgressCallback? onReceiveProgress,
     138             :   }) {
     139           2 :     return request<T>(
     140             :       path,
     141             :       data: data,
     142             :       queryParameters: queryParameters,
     143           2 :       options: checkOptions('PUT', options),
     144             :       cancelToken: cancelToken,
     145             :       onSendProgress: onSendProgress,
     146             :       onReceiveProgress: onReceiveProgress,
     147             :     );
     148             :   }
     149             : 
     150             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.requestOptions].
     151           1 :   @override
     152             :   Future<Response<T>> putUri<T>(
     153             :     Uri uri, {
     154             :     data,
     155             :     Options? options,
     156             :     CancelToken? cancelToken,
     157             :     ProgressCallback? onSendProgress,
     158             :     ProgressCallback? onReceiveProgress,
     159             :   }) {
     160           1 :     return requestUri<T>(
     161             :       uri,
     162             :       data: data,
     163           1 :       options: checkOptions('PUT', options),
     164             :       cancelToken: cancelToken,
     165             :       onSendProgress: onSendProgress,
     166             :       onReceiveProgress: onReceiveProgress,
     167             :     );
     168             :   }
     169             : 
     170             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.requestOptions].
     171           0 :   @override
     172             :   Future<Response<T>> head<T>(
     173             :     String path, {
     174             :     data,
     175             :     Map<String, dynamic>? queryParameters,
     176             :     Options? options,
     177             :     CancelToken? cancelToken,
     178             :   }) {
     179           0 :     return request<T>(
     180             :       path,
     181             :       data: data,
     182             :       queryParameters: queryParameters,
     183           0 :       options: checkOptions('HEAD', options),
     184             :       cancelToken: cancelToken,
     185             :     );
     186             :   }
     187             : 
     188             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.requestOptions].
     189           0 :   @override
     190             :   Future<Response<T>> headUri<T>(
     191             :     Uri uri, {
     192             :     data,
     193             :     Options? options,
     194             :     CancelToken? cancelToken,
     195             :   }) {
     196           0 :     return requestUri<T>(
     197             :       uri,
     198             :       data: data,
     199           0 :       options: checkOptions('HEAD', options),
     200             :       cancelToken: cancelToken,
     201             :     );
     202             :   }
     203             : 
     204             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.requestOptions].
     205           1 :   @override
     206             :   Future<Response<T>> delete<T>(
     207             :     String path, {
     208             :     data,
     209             :     Map<String, dynamic>? queryParameters,
     210             :     Options? options,
     211             :     CancelToken? cancelToken,
     212             :   }) {
     213           1 :     return request<T>(
     214             :       path,
     215             :       data: data,
     216             :       queryParameters: queryParameters,
     217           1 :       options: checkOptions('DELETE', options),
     218             :       cancelToken: cancelToken,
     219             :     );
     220             :   }
     221             : 
     222             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.requestOptions].
     223           1 :   @override
     224             :   Future<Response<T>> deleteUri<T>(
     225             :     Uri uri, {
     226             :     data,
     227             :     Options? options,
     228             :     CancelToken? cancelToken,
     229             :   }) {
     230           1 :     return requestUri<T>(
     231             :       uri,
     232             :       data: data,
     233           1 :       options: checkOptions('DELETE', options),
     234             :       cancelToken: cancelToken,
     235             :     );
     236             :   }
     237             : 
     238             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.requestOptions].
     239           1 :   @override
     240             :   Future<Response<T>> patch<T>(
     241             :     String path, {
     242             :     data,
     243             :     Map<String, dynamic>? queryParameters,
     244             :     Options? options,
     245             :     CancelToken? cancelToken,
     246             :     ProgressCallback? onSendProgress,
     247             :     ProgressCallback? onReceiveProgress,
     248             :   }) {
     249           1 :     return request<T>(
     250             :       path,
     251             :       data: data,
     252             :       queryParameters: queryParameters,
     253           1 :       options: checkOptions('PATCH', options),
     254             :       cancelToken: cancelToken,
     255             :       onSendProgress: onSendProgress,
     256             :       onReceiveProgress: onReceiveProgress,
     257             :     );
     258             :   }
     259             : 
     260             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.requestOptions].
     261           1 :   @override
     262             :   Future<Response<T>> patchUri<T>(
     263             :     Uri uri, {
     264             :     data,
     265             :     Options? options,
     266             :     CancelToken? cancelToken,
     267             :     ProgressCallback? onSendProgress,
     268             :     ProgressCallback? onReceiveProgress,
     269             :   }) {
     270           1 :     return requestUri<T>(
     271             :       uri,
     272             :       data: data,
     273           1 :       options: checkOptions('PATCH', options),
     274             :       cancelToken: cancelToken,
     275             :       onSendProgress: onSendProgress,
     276             :       onReceiveProgress: onReceiveProgress,
     277             :     );
     278             :   }
     279             : 
     280             :   /// Lock the current Dio instance.
     281             :   ///
     282             :   /// Dio will enqueue the incoming request tasks instead
     283             :   /// send them directly when [interceptor.requestOptions] is locked.
     284           0 :   @override
     285             :   void lock() {
     286           0 :     interceptors.requestLock.lock();
     287             :   }
     288             : 
     289             :   /// Unlock the current Dio instance.
     290             :   ///
     291             :   /// Dio instance dequeue the request task。
     292           0 :   @override
     293             :   void unlock() {
     294           0 :     interceptors.requestLock.unlock();
     295             :   }
     296             : 
     297             :   ///Clear the current Dio instance waiting queue.
     298           0 :   @override
     299             :   void clear() {
     300           0 :     interceptors.requestLock.clear();
     301             :   }
     302             : 
     303             :   ///  Download the file and save it in local. The default http method is 'GET',
     304             :   ///  you can custom it by [Options.method].
     305             :   ///
     306             :   ///  [urlPath]: The file url.
     307             :   ///
     308             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     309             :   ///  a callback:
     310             :   ///  1. A path with String type, eg 'xs.jpg'
     311             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     312             :   ///  ```dart
     313             :   ///   await dio.download(url,(HttpHeaders responseHeaders){
     314             :   ///      ...
     315             :   ///      return '...';
     316             :   ///    });
     317             :   ///  ```
     318             :   ///
     319             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     320             :   ///  please refer to [ProgressCallback].
     321             :   ///
     322             :   /// [deleteOnError] Whether delete the file when error occurs. The default value is [true].
     323             :   ///
     324             :   ///  [lengthHeader] : The real size of original file (not compressed).
     325             :   ///  When file is compressed:
     326             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     327             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     328             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     329             :   ///
     330             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     331             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     332             :   ///
     333             :   ///     await dio.download(url, './example/flutter.svg',
     334             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}),  // disable gzip
     335             :   ///     onProgress: (received, total) {
     336             :   ///       if (total != -1) {
     337             :   ///        print((received / total * 100).toStringAsFixed(0) + '%');
     338             :   ///       }
     339             :   ///     });
     340             : 
     341             :   @override
     342           0 :   Future<Response> download(
     343             :     String urlPath,
     344             :     savePath, {
     345             :     ProgressCallback? onReceiveProgress,
     346             :     Map<String, dynamic>? queryParameters,
     347             :     CancelToken? cancelToken,
     348             :     bool deleteOnError = true,
     349             :     String lengthHeader = Headers.contentLengthHeader,
     350             :     data,
     351             :     Options? options,
     352             :   }) async {
     353           0 :     throw UnsupportedError('Unsupport download API in browser');
     354             :   }
     355             : 
     356             :   ///  Download the file and save it in local. The default http method is 'GET',
     357             :   ///  you can custom it by [Options.method].
     358             :   ///
     359             :   ///  [uri]: The file url.
     360             :   ///
     361             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     362             :   ///  a callback:
     363             :   ///  1. A path with String type, eg 'xs.jpg'
     364             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     365             :   ///  ```dart
     366             :   ///   await dio.downloadUri(uri,(HttpHeaders responseHeaders){
     367             :   ///      ...
     368             :   ///      return '...';
     369             :   ///    });
     370             :   ///  ```
     371             :   ///
     372             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     373             :   ///  please refer to [ProgressCallback].
     374             :   ///
     375             :   ///  [lengthHeader] : The real size of original file (not compressed).
     376             :   ///  When file is compressed:
     377             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     378             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     379             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     380             :   ///
     381             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     382             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     383             :   ///
     384             :   ///     await dio.downloadUri(uri, './example/flutter.svg',
     385             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}),  // disable gzip
     386             :   ///     onProgress: (received, total) {
     387             :   ///       if (total != -1) {
     388             :   ///        print((received / total * 100).toStringAsFixed(0) + '%');
     389             :   ///       }
     390             :   ///     });
     391           0 :   @override
     392             :   Future<Response> downloadUri(
     393             :     Uri uri,
     394             :     savePath, {
     395             :     ProgressCallback? onReceiveProgress,
     396             :     CancelToken? cancelToken,
     397             :     bool deleteOnError = true,
     398             :     String lengthHeader = Headers.contentLengthHeader,
     399             :     data,
     400             :     Options? options,
     401             :   }) {
     402           0 :     return download(
     403           0 :       uri.toString(),
     404             :       savePath,
     405             :       onReceiveProgress: onReceiveProgress,
     406             :       lengthHeader: lengthHeader,
     407             :       deleteOnError: deleteOnError,
     408             :       cancelToken: cancelToken,
     409             :       data: data,
     410             :       options: options,
     411             :     );
     412             :   }
     413             : 
     414             :   /// Make http request with options.
     415             :   ///
     416             :   /// [uri] The uri.
     417             :   /// [data] The request data
     418             :   /// [options] The request options.
     419           1 :   @override
     420             :   Future<Response<T>> requestUri<T>(
     421             :     Uri uri, {
     422             :     data,
     423             :     CancelToken? cancelToken,
     424             :     Options? options,
     425             :     ProgressCallback? onSendProgress,
     426             :     ProgressCallback? onReceiveProgress,
     427             :   }) {
     428           1 :     return request(
     429           1 :       uri.toString(),
     430             :       data: data,
     431             :       cancelToken: cancelToken,
     432             :       options: options,
     433             :       onSendProgress: onSendProgress,
     434             :       onReceiveProgress: onReceiveProgress,
     435             :     );
     436             :   }
     437             : 
     438             :   /// Make http request with options.
     439             :   ///
     440             :   /// [path] The url path.
     441             :   /// [data] The request data
     442             :   /// [options] The request options.
     443             :   @override
     444           8 :   Future<Response<T>> request<T>(
     445             :     String path, {
     446             :     data,
     447             :     Map<String, dynamic>? queryParameters,
     448             :     CancelToken? cancelToken,
     449             :     Options? options,
     450             :     ProgressCallback? onSendProgress,
     451             :     ProgressCallback? onReceiveProgress,
     452             :   }) async {
     453           0 :     options ??= Options();
     454           8 :     var requestOptions = options.compose(
     455           8 :       this.options,
     456             :       path,
     457             :       data: data,
     458             :       queryParameters: queryParameters,
     459             :       onReceiveProgress: onReceiveProgress,
     460             :       onSendProgress: onSendProgress,
     461             :       cancelToken: cancelToken,
     462             :     );
     463           8 :     requestOptions.onReceiveProgress = onReceiveProgress;
     464           8 :     requestOptions.onSendProgress = onSendProgress;
     465           8 :     requestOptions.cancelToken = cancelToken;
     466             : 
     467           8 :     if (_closed) {
     468           0 :       throw DioError(
     469             :         requestOptions: requestOptions,
     470             :         error: "Dio can't establish new connection after closed.",
     471             :       );
     472             :     }
     473             : 
     474           8 :     return fetch<T>(requestOptions);
     475             :   }
     476             : 
     477             :   @override
     478           8 :   Future<Response<T>> fetch<T>(RequestOptions requestOptions) async {
     479           8 :     final stackTrace = StackTrace.current;
     480             : 
     481           8 :     if (requestOptions.cancelToken != null) {
     482           4 :       requestOptions.cancelToken!.requestOptions = requestOptions;
     483             :     }
     484             : 
     485           8 :     if (T != dynamic &&
     486           4 :         !(requestOptions.responseType == ResponseType.bytes ||
     487           4 :             requestOptions.responseType == ResponseType.stream)) {
     488           1 :       if (T == String) {
     489           1 :         requestOptions.responseType = ResponseType.plain;
     490             :       } else {
     491           1 :         requestOptions.responseType = ResponseType.json;
     492             :       }
     493             :     }
     494             : 
     495             :     // Convert the request interceptor to a functional callback in which
     496             :     // we can handle the return value of interceptor callback.
     497           8 :     FutureOr Function(dynamic) _requestInterceptorWrapper(
     498             :       InterceptorSendCallback interceptor,
     499             :     ) {
     500           8 :       return (dynamic _state) async {
     501             :         var state = _state as InterceptorState;
     502          16 :         if (state.type == InterceptorResultType.next) {
     503           8 :           return listenCancelForAsyncTask(
     504           8 :             requestOptions.cancelToken,
     505          16 :             Future(() {
     506          32 :               return checkIfNeedEnqueue(interceptors.requestLock, () {
     507           8 :                 var requestHandler = RequestInterceptorHandler();
     508           8 :                 interceptor(state.data as RequestOptions, requestHandler);
     509           8 :                 return requestHandler.future;
     510             :               });
     511             :             }),
     512             :           );
     513             :         } else {
     514             :           return state;
     515             :         }
     516             :       };
     517             :     }
     518             : 
     519             :     // Convert the response interceptor to a functional callback in which
     520             :     // we can handle the return value of interceptor callback.
     521           2 :     FutureOr<dynamic> Function(dynamic) _responseInterceptorWrapper(
     522             :       InterceptorSuccessCallback interceptor,
     523             :     ) {
     524           2 :       return (_state) async {
     525             :         var state = _state as InterceptorState;
     526           4 :         if (state.type == InterceptorResultType.next ||
     527           4 :             state.type == InterceptorResultType.resolveCallFollowing) {
     528           2 :           return listenCancelForAsyncTask(
     529           2 :             requestOptions.cancelToken,
     530           4 :             Future(() {
     531           8 :               return checkIfNeedEnqueue(interceptors.responseLock, () {
     532           2 :                 var responseHandler = ResponseInterceptorHandler();
     533           2 :                 interceptor(state.data as Response, responseHandler);
     534           2 :                 return responseHandler.future;
     535             :               });
     536             :             }),
     537             :           );
     538             :         } else {
     539             :           return state;
     540             :         }
     541             :       };
     542             :     }
     543             : 
     544             :     // Convert the error interceptor to a functional callback in which
     545             :     // we can handle the return value of interceptor callback.
     546           2 :     FutureOr<dynamic> Function(dynamic, StackTrace) _errorInterceptorWrapper(
     547             :         InterceptorErrorCallback interceptor) {
     548           2 :       return (err, stackTrace) {
     549           2 :         if (err is! InterceptorState) {
     550           1 :           err = InterceptorState(
     551           1 :             assureDioError(
     552             :               err,
     553             :               requestOptions,
     554             :             ),
     555             :           );
     556             :         }
     557             : 
     558           4 :         if (err.type == InterceptorResultType.next ||
     559           4 :             err.type == InterceptorResultType.rejectCallFollowing) {
     560           2 :           return listenCancelForAsyncTask(
     561           2 :             requestOptions.cancelToken,
     562           4 :             Future(() {
     563           8 :               return checkIfNeedEnqueue(interceptors.errorLock, () {
     564           2 :                 var errorHandler = ErrorInterceptorHandler();
     565           2 :                 interceptor(err.data as DioError, errorHandler);
     566           2 :                 return errorHandler.future;
     567             :               });
     568             :             }),
     569             :           );
     570             :         } else {
     571             :           throw err;
     572             :         }
     573             :       };
     574             :     }
     575             : 
     576             :     // Build a request flow in which the processors(interceptors)
     577             :     // execute in FIFO order.
     578             : 
     579             :     // Start the request flow
     580          24 :     var future = Future<dynamic>(() => InterceptorState(requestOptions));
     581             : 
     582             :     // Add request interceptors to request flow
     583          18 :     interceptors.forEach((Interceptor interceptor) {
     584           2 :       var fun = interceptor is QueuedInterceptor
     585           1 :           ? interceptor._handleRequest
     586           2 :           : interceptor.onRequest;
     587           2 :       future = future.then(_requestInterceptorWrapper(fun));
     588             :     });
     589             : 
     590             :     // Add dispatching callback to request flow
     591          16 :     future = future.then(_requestInterceptorWrapper((
     592             :       RequestOptions reqOpt,
     593             :       RequestInterceptorHandler handler,
     594             :     ) {
     595             :       requestOptions = reqOpt;
     596           8 :       _dispatchRequest(reqOpt)
     597          22 :           .then((value) => handler.resolve(value, true))
     598          14 :           .catchError((e) {
     599           6 :         handler.reject(e as DioError, true);
     600             :       });
     601             :     }));
     602             : 
     603             :     // Add response interceptors to request flow
     604          18 :     interceptors.forEach((Interceptor interceptor) {
     605           2 :       var fun = interceptor is QueuedInterceptor
     606           1 :           ? interceptor._handleResponse
     607           2 :           : interceptor.onResponse;
     608           2 :       future = future.then(_responseInterceptorWrapper(fun));
     609             :     });
     610             : 
     611             :     // Add error handlers to request flow
     612          18 :     interceptors.forEach((Interceptor interceptor) {
     613           2 :       var fun = interceptor is QueuedInterceptor
     614           1 :           ? interceptor._handleError
     615           2 :           : interceptor.onError;
     616           2 :       future = future.catchError(_errorInterceptorWrapper(fun));
     617             :     });
     618             : 
     619             :     // Normalize errors, we convert error to the DioError
     620          15 :     return future.then<Response<T>>((data) {
     621           7 :       return assureResponse<T>(
     622          14 :         data is InterceptorState ? data.data : data,
     623             :         requestOptions,
     624             :       );
     625          14 :     }).catchError((err, _) {
     626           6 :       var isState = err is InterceptorState;
     627             : 
     628             :       if (isState) {
     629          12 :         if ((err as InterceptorState).type == InterceptorResultType.resolve) {
     630           0 :           return assureResponse<T>(err.data, requestOptions);
     631             :         }
     632             :       }
     633             : 
     634           6 :       throw assureDioError(
     635           6 :         isState ? err.data : err,
     636             :         requestOptions,
     637             :         stackTrace,
     638             :       );
     639             :     });
     640             :   }
     641             : 
     642             :   // Initiate Http requests
     643           8 :   Future<Response<T>> _dispatchRequest<T>(RequestOptions reqOpt) async {
     644           8 :     var cancelToken = reqOpt.cancelToken;
     645             :     ResponseBody responseBody;
     646             :     try {
     647          16 :       var stream = await _transformData(reqOpt);
     648          24 :       responseBody = await httpClientAdapter.fetch(
     649             :         reqOpt,
     650             :         stream,
     651           2 :         cancelToken?.whenCancel,
     652             :       );
     653          14 :       responseBody.headers = responseBody.headers;
     654          14 :       var headers = Headers.fromMap(responseBody.headers);
     655           7 :       var ret = Response<T>(
     656             :         headers: headers,
     657             :         requestOptions: reqOpt,
     658           9 :         redirects: responseBody.redirects ?? [],
     659           7 :         isRedirect: responseBody.isRedirect,
     660           7 :         statusCode: responseBody.statusCode,
     661           7 :         statusMessage: responseBody.statusMessage,
     662           7 :         extra: responseBody.extra,
     663             :       );
     664          14 :       var statusOk = reqOpt.validateStatus(responseBody.statusCode);
     665           8 :       if (statusOk || reqOpt.receiveDataWhenStatusError == true) {
     666           7 :         var forceConvert = !(T == dynamic || T == String) &&
     667           0 :             !(reqOpt.responseType == ResponseType.bytes ||
     668           0 :                 reqOpt.responseType == ResponseType.stream);
     669             :         String? contentType;
     670             :         if (forceConvert) {
     671           0 :           contentType = headers.value(Headers.contentTypeHeader);
     672           0 :           headers.set(Headers.contentTypeHeader, Headers.jsonContentType);
     673             :         }
     674           7 :         ret.data =
     675          21 :             (await transformer.transformResponse(reqOpt, responseBody)) as T?;
     676             :         if (forceConvert) {
     677           0 :           headers.set(Headers.contentTypeHeader, contentType);
     678             :         }
     679             :       } else {
     680           4 :         await responseBody.stream.listen(null).cancel();
     681             :       }
     682           7 :       checkCancelled(cancelToken);
     683             :       if (statusOk) {
     684          28 :         return checkIfNeedEnqueue(interceptors.responseLock, () => ret);
     685             :       } else {
     686           4 :         throw DioError(
     687             :           requestOptions: reqOpt,
     688             :           response: ret,
     689           8 :           error: 'Http status error [${responseBody.statusCode}]',
     690             :           type: DioErrorType.response,
     691             :         );
     692             :       }
     693             :     } catch (e) {
     694           6 :       throw assureDioError(e, reqOpt);
     695             :     }
     696             :   }
     697             : 
     698           8 :   Future<Stream<Uint8List>?> _transformData(RequestOptions options) async {
     699           8 :     var data = options.data;
     700             :     List<int> bytes;
     701             :     Stream<List<int>> stream;
     702             :     const allowPayloadMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
     703           8 :     if (data != null && allowPayloadMethods.contains(options.method)) {
     704             :       // Handle the FormData
     705             :       int? length;
     706           4 :       if (data is Stream) {
     707           1 :         assert(data is Stream<List>,
     708           0 :             'Stream type must be `Stream<List>`, but ${data.runtimeType} is found.');
     709             :         stream = data as Stream<List<int>>;
     710           4 :         options.headers.keys.any((String key) {
     711           2 :           if (key.toLowerCase() == Headers.contentLengthHeader) {
     712           4 :             length = int.parse(options.headers[key].toString());
     713             :             return true;
     714             :           }
     715             :           return false;
     716             :         });
     717           3 :       } else if (data is FormData) {
     718           0 :         options.headers[Headers.contentTypeHeader] =
     719           0 :             'multipart/form-data; boundary=${data.boundary}';
     720             : 
     721           0 :         stream = data.finalize();
     722           0 :         length = data.length;
     723           0 :         options.headers[Headers.contentLengthHeader] = length.toString();
     724             :       } else {
     725             :         // Call request transformer.
     726           9 :         var _data = await transformer.transformRequest(options);
     727             : 
     728           3 :         if (options.requestEncoder != null) {
     729           0 :           bytes = options.requestEncoder!(_data, options);
     730             :         } else {
     731             :           //Default convert to utf8
     732           3 :           bytes = utf8.encode(_data);
     733             :         }
     734             :         // support data sending progress
     735           3 :         length = bytes.length;
     736           9 :         options.headers[Headers.contentLengthHeader] = length.toString();
     737             : 
     738           3 :         var group = <List<int>>[];
     739             :         const size = 1024;
     740           9 :         var groupCount = (bytes.length / size).ceil();
     741           5 :         for (var i = 0; i < groupCount; ++i) {
     742           2 :           var start = i * size;
     743           9 :           group.add(bytes.sublist(start, math.min(start + size, bytes.length)));
     744             :         }
     745           3 :         stream = Stream.fromIterable(group);
     746             :       }
     747           4 :       return addProgress(stream, length, options);
     748             :     }
     749             :     return null;
     750             :   }
     751             : 
     752             :   // If the request has been cancelled, stop request and throw error.
     753           7 :   static void checkCancelled(CancelToken? cancelToken) {
     754           1 :     if (cancelToken != null && cancelToken.cancelError != null) {
     755           0 :       throw cancelToken.cancelError!;
     756             :     }
     757             :   }
     758             : 
     759           8 :   static Future<T> listenCancelForAsyncTask<T>(
     760             :       CancelToken? cancelToken, Future<T> future) {
     761          16 :     return Future.any([
     762           6 :       if (cancelToken != null) cancelToken.whenCancel.then((e) => throw e),
     763           8 :       future,
     764             :     ]);
     765             :   }
     766             : 
     767           8 :   static Options checkOptions(String method, Options? options) {
     768           7 :     options ??= Options();
     769           8 :     options.method = method;
     770             :     return options;
     771             :   }
     772             : 
     773           8 :   static FutureOr<T> checkIfNeedEnqueue<T>(
     774             :     Lock lock,
     775             :     _WaitCallback<T> callback,
     776             :   ) {
     777           8 :     if (lock.locked) {
     778           0 :       return lock._wait(callback)!;
     779             :     } else {
     780             :       return callback();
     781             :     }
     782             :   }
     783             : 
     784           6 :   static DioError assureDioError(
     785             :     err,
     786             :     RequestOptions requestOptions, [
     787             :     StackTrace? sourceStackTrace,
     788             :   ]) {
     789             :     DioError dioError;
     790           6 :     if (err is DioError) {
     791             :       dioError = err;
     792             :     } else {
     793           3 :       dioError = DioError(requestOptions: requestOptions, error: err);
     794             :     }
     795             : 
     796          12 :     dioError.stackTrace = sourceStackTrace ?? dioError.stackTrace;
     797             : 
     798             :     return dioError;
     799             :   }
     800             : 
     801           8 :   static Response<T> assureResponse<T>(response,
     802             :       [RequestOptions? requestOptions]) {
     803           8 :     if (response is! Response) {
     804           0 :       return Response<T>(
     805             :         data: response as T,
     806           0 :         requestOptions: requestOptions ?? RequestOptions(path: ''),
     807             :       );
     808           8 :     } else if (response is! Response<T>) {
     809           3 :       T? data = response.data as T?;
     810           3 :       return Response<T>(
     811             :         data: data,
     812           3 :         headers: response.headers,
     813           3 :         requestOptions: response.requestOptions,
     814           3 :         statusCode: response.statusCode,
     815           3 :         isRedirect: response.isRedirect,
     816           3 :         redirects: response.redirects,
     817           3 :         statusMessage: response.statusMessage,
     818           3 :         extra: response.extra,
     819             :       );
     820             :     }
     821             :     return response;
     822             :   }
     823             : }

Generated by: LCOV version 1.14