LCOV - code coverage report
Current view: top level - src - dio.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 200 252 79.4 %
Date: 2020-02-27 17:47:50 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : import 'dart:convert';
       3             : import 'dart:math' as math;
       4             : import 'dart:typed_data';
       5             : import 'adapter.dart';
       6             : import 'form_data.dart';
       7             : import 'options.dart';
       8             : import 'interceptor.dart';
       9             : import 'headers.dart';
      10             : import 'cancel_token.dart';
      11             : import 'transformer.dart';
      12             : import 'response.dart';
      13             : import 'dio_error.dart';
      14             : import 'entry_stub.dart'
      15             : // ignore: uri_does_not_exist
      16             :     if (dart.library.html) 'entry/dio_for_browser.dart'
      17             : // ignore: uri_does_not_exist
      18             :     if (dart.library.io) 'entry/dio_for_native.dart';
      19             : 
      20             : /// A powerful Http client for Dart, which supports Interceptors,
      21             : /// Global configuration, FormData, File downloading etc. and Dio is
      22             : /// very easy to use.
      23             : ///
      24             : /// You can create a dio instance and config it by two ways:
      25             : /// 1. create first , then config it
      26             : ///
      27             : ///   ```dart
      28             : ///    var dio = Dio();
      29             : ///    dio.options.baseUrl = "http://www.dtworkroom.com/doris/1/2.0.0/";
      30             : ///    dio.options.connectTimeout = 5000; //5s
      31             : ///    dio.options.receiveTimeout = 5000;
      32             : ///    dio.options.headers = {HttpHeaders.userAgentHeader: 'dio', 'common-header': 'xx'};
      33             : ///   ```
      34             : /// 2. create and config it:
      35             : ///
      36             : /// ```dart
      37             : ///   var dio = Dio(BaseOptions(
      38             : ///    baseUrl: "http://www.dtworkroom.com/doris/1/2.0.0/",
      39             : ///    connectTimeout: 5000,
      40             : ///    receiveTimeout: 5000,
      41             : ///    headers: {HttpHeaders.userAgentHeader: 'dio', 'common-header': 'xx'},
      42             : ///   ));
      43             : ///  ```
      44             : 
      45             : abstract class Dio {
      46          10 :   factory Dio([BaseOptions options]) => createDio(options);
      47             : 
      48             :   /// Default Request config. More see [BaseOptions] .
      49             :   BaseOptions options;
      50             : 
      51             :   Interceptors get interceptors;
      52             : 
      53             :   HttpClientAdapter httpClientAdapter;
      54             : 
      55             :   /// [transformer] allows changes to the request/response data before it is sent/received to/from the server
      56             :   /// This is only applicable for request methods 'PUT', 'POST', and 'PATCH'.
      57             :   Transformer transformer;
      58             : 
      59             :   /// Shuts down the dio client.
      60             :   ///
      61             :   /// If [force] is `false` (the default) the [Dio] will be kept alive
      62             :   /// until all active connections are done. If [force] is `true` any active
      63             :   /// connections will be closed to immediately release all resources. These
      64             :   /// closed connections will receive an error event to indicate that the client
      65             :   /// was shut down. In both cases trying to establish a new connection after
      66             :   /// calling [close] will throw an exception.
      67             :   void close({bool force = false});
      68             : 
      69             :   /// Handy method to make http GET request, which is a alias of  [BaseDio.request].
      70             :   Future<Response<T>> get<T>(
      71             :     String path, {
      72             :     Map<String, dynamic> queryParameters,
      73             :     Options options,
      74             :     CancelToken cancelToken,
      75             :     ProgressCallback onReceiveProgress,
      76             :   });
      77             : 
      78             :   /// Handy method to make http GET request, which is a alias of [BaseDio.request].
      79             :   Future<Response<T>> getUri<T>(
      80             :     Uri uri, {
      81             :     Options options,
      82             :     CancelToken cancelToken,
      83             :     ProgressCallback onReceiveProgress,
      84             :   });
      85             : 
      86             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.request].
      87             :   Future<Response<T>> post<T>(
      88             :     String path, {
      89             :     data,
      90             :     Map<String, dynamic> queryParameters,
      91             :     Options options,
      92             :     CancelToken cancelToken,
      93             :     ProgressCallback onSendProgress,
      94             :     ProgressCallback onReceiveProgress,
      95             :   });
      96             : 
      97             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.request].
      98             :   Future<Response<T>> postUri<T>(
      99             :     Uri uri, {
     100             :     data,
     101             :     Options options,
     102             :     CancelToken cancelToken,
     103             :     ProgressCallback onSendProgress,
     104             :     ProgressCallback onReceiveProgress,
     105             :   });
     106             : 
     107             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.request].
     108             :   Future<Response<T>> put<T>(
     109             :     String path, {
     110             :     data,
     111             :     Map<String, dynamic> queryParameters,
     112             :     Options options,
     113             :     CancelToken cancelToken,
     114             :     ProgressCallback onSendProgress,
     115             :     ProgressCallback onReceiveProgress,
     116             :   });
     117             : 
     118             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.request].
     119             :   Future<Response<T>> putUri<T>(
     120             :     Uri uri, {
     121             :     data,
     122             :     Options options,
     123             :     CancelToken cancelToken,
     124             :     ProgressCallback onSendProgress,
     125             :     ProgressCallback onReceiveProgress,
     126             :   });
     127             : 
     128             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.request].
     129             :   Future<Response<T>> head<T>(
     130             :     String path, {
     131             :     data,
     132             :     Map<String, dynamic> queryParameters,
     133             :     Options options,
     134             :     CancelToken cancelToken,
     135             :   });
     136             : 
     137             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.request].
     138             :   Future<Response<T>> headUri<T>(
     139             :     Uri uri, {
     140             :     data,
     141             :     Options options,
     142             :     CancelToken cancelToken,
     143             :   });
     144             : 
     145             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.request].
     146             :   Future<Response<T>> delete<T>(
     147             :     String path, {
     148             :     data,
     149             :     Map<String, dynamic> queryParameters,
     150             :     Options options,
     151             :     CancelToken cancelToken,
     152             :   });
     153             : 
     154             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.request].
     155             :   Future<Response<T>> deleteUri<T>(
     156             :     Uri uri, {
     157             :     data,
     158             :     Options options,
     159             :     CancelToken cancelToken,
     160             :   });
     161             : 
     162             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.request].
     163             :   Future<Response<T>> patch<T>(
     164             :     String path, {
     165             :     data,
     166             :     Map<String, dynamic> queryParameters,
     167             :     Options options,
     168             :     CancelToken cancelToken,
     169             :     ProgressCallback onSendProgress,
     170             :     ProgressCallback onReceiveProgress,
     171             :   });
     172             : 
     173             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.request].
     174             :   Future<Response<T>> patchUri<T>(
     175             :     Uri uri, {
     176             :     data,
     177             :     Options options,
     178             :     CancelToken cancelToken,
     179             :     ProgressCallback onSendProgress,
     180             :     ProgressCallback onReceiveProgress,
     181             :   });
     182             : 
     183             :   /// Assure the final future state is succeed!
     184             :   Future<Response<T>> resolve<T>(response);
     185             : 
     186             :   /// Assure the final future state is failed!
     187             :   Future<Response<T>> reject<T>(err);
     188             : 
     189             :   /// Lock the current Dio instance.
     190             :   ///
     191             :   /// Dio will enqueue the incoming request tasks instead
     192             :   /// send them directly when [interceptor.request] is locked.
     193             : 
     194             :   void lock();
     195             : 
     196             :   /// Unlock the current Dio instance.
     197             :   ///
     198             :   /// Dio instance dequeue the request task。
     199             :   void unlock();
     200             : 
     201             :   ///Clear the current Dio instance waiting queue.
     202             : 
     203             :   void clear();
     204             : 
     205             :   ///  Download the file and save it in local. The default http method is "GET",
     206             :   ///  you can custom it by [Options.method].
     207             :   ///
     208             :   ///  [urlPath]: The file url.
     209             :   ///
     210             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     211             :   ///  a callback:
     212             :   ///  1. A path with String type, eg "xs.jpg"
     213             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     214             :   ///  ```dart
     215             :   ///   await dio.download(url,(HttpHeaders responseHeaders){
     216             :   ///      ...
     217             :   ///      return "...";
     218             :   ///    });
     219             :   ///  ```
     220             :   ///
     221             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     222             :   ///  please refer to [ProgressCallback].
     223             :   ///
     224             :   /// [deleteOnError] Whether delete the file when error occurs. The default value is [true].
     225             :   ///
     226             :   ///  [lengthHeader] : The real size of original file (not compressed).
     227             :   ///  When file is compressed:
     228             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     229             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     230             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     231             :   ///
     232             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     233             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     234             :   ///
     235             :   ///     await dio.download(url, "./example/flutter.svg",
     236             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}),  // disable gzip
     237             :   ///     onProgress: (received, total) {
     238             :   ///       if (total != -1) {
     239             :   ///        print((received / total * 100).toStringAsFixed(0) + "%");
     240             :   ///       }
     241             :   ///     });
     242             : 
     243             :   Future<Response> download(
     244             :     String urlPath,
     245             :     savePath, {
     246             :     ProgressCallback onReceiveProgress,
     247             :     Map<String, dynamic> queryParameters,
     248             :     CancelToken cancelToken,
     249             :     bool deleteOnError = true,
     250             :     String lengthHeader = Headers.contentLengthHeader,
     251             :     data,
     252             :     Options options,
     253             :   });
     254             : 
     255             :   ///  Download the file and save it in local. The default http method is "GET",
     256             :   ///  you can custom it by [Options.method].
     257             :   ///
     258             :   ///  [uri]: The file url.
     259             :   ///
     260             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     261             :   ///  a callback:
     262             :   ///  1. A path with String type, eg "xs.jpg"
     263             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     264             :   ///  ```dart
     265             :   ///   await dio.downloadUri(uri,(HttpHeaders responseHeaders){
     266             :   ///      ...
     267             :   ///      return "...";
     268             :   ///    });
     269             :   ///  ```
     270             :   ///
     271             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     272             :   ///  please refer to [ProgressCallback].
     273             :   ///
     274             :   ///  [lengthHeader] : The real size of original file (not compressed).
     275             :   ///  When file is compressed:
     276             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     277             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     278             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     279             :   ///
     280             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     281             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     282             :   ///
     283             :   ///     await dio.downloadUri(uri, "./example/flutter.svg",
     284             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}),  // disable gzip
     285             :   ///     onProgress: (received, total) {
     286             :   ///       if (total != -1) {
     287             :   ///        print((received / total * 100).toStringAsFixed(0) + "%");
     288             :   ///       }
     289             :   ///     });
     290             :   Future<Response> downloadUri(
     291             :     Uri uri,
     292             :     savePath, {
     293             :     ProgressCallback onReceiveProgress,
     294             :     CancelToken cancelToken,
     295             :     bool deleteOnError = true,
     296             :     String lengthHeader = Headers.contentLengthHeader,
     297             :     data,
     298             :     Options options,
     299             :   });
     300             : 
     301             :   /// Make http request with options.
     302             :   ///
     303             :   /// [path] The url path.
     304             :   /// [data] The request data
     305             :   /// [options] The request options.
     306             : 
     307             :   Future<Response<T>> request<T>(
     308             :     String path, {
     309             :     data,
     310             :     Map<String, dynamic> queryParameters,
     311             :     CancelToken cancelToken,
     312             :     Options options,
     313             :     ProgressCallback onSendProgress,
     314             :     ProgressCallback onReceiveProgress,
     315             :   });
     316             : 
     317             :   /// Make http request with options.
     318             :   ///
     319             :   /// [uri] The uri.
     320             :   /// [data] The request data
     321             :   /// [options] The request options.
     322             :   Future<Response<T>> requestUri<T>(
     323             :     Uri uri, {
     324             :     data,
     325             :     CancelToken cancelToken,
     326             :     Options options,
     327             :     ProgressCallback onSendProgress,
     328             :     ProgressCallback onReceiveProgress,
     329             :   });
     330             : }
     331             : 
     332             : abstract class DioMixin implements Dio {
     333             :   /// Default Request config. More see [BaseOptions].
     334             :   @override
     335             :   BaseOptions options;
     336             : 
     337             :   /// Each Dio instance has a interceptor by which you can intercept requests or responses before they are
     338             :   /// handled by `then` or `catchError`. the [interceptor] field
     339             :   /// contains a [RequestInterceptor] and a [ResponseInterceptor] instance.
     340             :   final Interceptors _interceptors = Interceptors();
     341             : 
     342           5 :   @override
     343           5 :   Interceptors get interceptors => _interceptors;
     344             : 
     345             :   @override
     346             :   HttpClientAdapter httpClientAdapter;
     347             : 
     348             :   @override
     349             :   Transformer transformer = DefaultTransformer();
     350             : 
     351             :   bool _closed = false;
     352             : 
     353           0 :   @override
     354             :   void close({bool force = false}) {
     355           0 :     _closed = true;
     356           0 :     httpClientAdapter.close(force: force);
     357             :   }
     358             : 
     359             :   /// Handy method to make http GET request, which is a alias of  [BaseDio.request].
     360           4 :   @override
     361             :   Future<Response<T>> get<T>(
     362             :     String path, {
     363             :     Map<String, dynamic> queryParameters,
     364             :     Options options,
     365             :     CancelToken cancelToken,
     366             :     ProgressCallback onReceiveProgress,
     367             :   }) {
     368           4 :     return request<T>(
     369             :       path,
     370             :       queryParameters: queryParameters,
     371           4 :       options: checkOptions('GET', options),
     372             :       onReceiveProgress: onReceiveProgress,
     373             :       cancelToken: cancelToken,
     374             :     );
     375             :   }
     376             : 
     377             :   /// Handy method to make http GET request, which is a alias of [BaseDio.request].
     378           1 :   @override
     379             :   Future<Response<T>> getUri<T>(
     380             :     Uri uri, {
     381             :     Options options,
     382             :     CancelToken cancelToken,
     383             :     ProgressCallback onReceiveProgress,
     384             :   }) {
     385           1 :     return requestUri<T>(
     386             :       uri,
     387           1 :       options: checkOptions('GET', options),
     388             :       onReceiveProgress: onReceiveProgress,
     389             :       cancelToken: cancelToken,
     390             :     );
     391             :   }
     392             : 
     393             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.request].
     394           1 :   @override
     395             :   Future<Response<T>> post<T>(
     396             :     String path, {
     397             :     data,
     398             :     Map<String, dynamic> queryParameters,
     399             :     Options options,
     400             :     CancelToken cancelToken,
     401             :     ProgressCallback onSendProgress,
     402             :     ProgressCallback onReceiveProgress,
     403             :   }) {
     404           1 :     return request<T>(
     405             :       path,
     406             :       data: data,
     407           1 :       options: checkOptions('POST', options),
     408             :       queryParameters: queryParameters,
     409             :       cancelToken: cancelToken,
     410             :       onSendProgress: onSendProgress,
     411             :       onReceiveProgress: onReceiveProgress,
     412             :     );
     413             :   }
     414             : 
     415             :   /// Handy method to make http POST request, which is a alias of  [BaseDio.request].
     416           1 :   @override
     417             :   Future<Response<T>> postUri<T>(
     418             :     Uri uri, {
     419             :     data,
     420             :     Options options,
     421             :     CancelToken cancelToken,
     422             :     ProgressCallback onSendProgress,
     423             :     ProgressCallback onReceiveProgress,
     424             :   }) {
     425           1 :     return requestUri<T>(
     426             :       uri,
     427             :       data: data,
     428           1 :       options: checkOptions('POST', options),
     429             :       cancelToken: cancelToken,
     430             :       onSendProgress: onSendProgress,
     431             :       onReceiveProgress: onReceiveProgress,
     432             :     );
     433             :   }
     434             : 
     435             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.request].
     436           1 :   @override
     437             :   Future<Response<T>> put<T>(
     438             :     String path, {
     439             :     data,
     440             :     Map<String, dynamic> queryParameters,
     441             :     Options options,
     442             :     CancelToken cancelToken,
     443             :     ProgressCallback onSendProgress,
     444             :     ProgressCallback onReceiveProgress,
     445             :   }) {
     446           1 :     return request<T>(
     447             :       path,
     448             :       data: data,
     449             :       queryParameters: queryParameters,
     450           1 :       options: checkOptions('PUT', options),
     451             :       cancelToken: cancelToken,
     452             :       onSendProgress: onSendProgress,
     453             :       onReceiveProgress: onReceiveProgress,
     454             :     );
     455             :   }
     456             : 
     457             :   /// Handy method to make http PUT request, which is a alias of  [BaseDio.request].
     458           1 :   @override
     459             :   Future<Response<T>> putUri<T>(
     460             :     Uri uri, {
     461             :     data,
     462             :     Options options,
     463             :     CancelToken cancelToken,
     464             :     ProgressCallback onSendProgress,
     465             :     ProgressCallback onReceiveProgress,
     466             :   }) {
     467           1 :     return requestUri<T>(
     468             :       uri,
     469             :       data: data,
     470           1 :       options: checkOptions('PUT', options),
     471             :       cancelToken: cancelToken,
     472             :       onSendProgress: onSendProgress,
     473             :       onReceiveProgress: onReceiveProgress,
     474             :     );
     475             :   }
     476             : 
     477             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.request].
     478           0 :   @override
     479             :   Future<Response<T>> head<T>(
     480             :     String path, {
     481             :     data,
     482             :     Map<String, dynamic> queryParameters,
     483             :     Options options,
     484             :     CancelToken cancelToken,
     485             :   }) {
     486           0 :     return request<T>(
     487             :       path,
     488             :       data: data,
     489             :       queryParameters: queryParameters,
     490           0 :       options: checkOptions('HEAD', options),
     491             :       cancelToken: cancelToken,
     492             :     );
     493             :   }
     494             : 
     495             :   /// Handy method to make http HEAD request, which is a alias of [BaseDio.request].
     496           0 :   @override
     497             :   Future<Response<T>> headUri<T>(
     498             :     Uri uri, {
     499             :     data,
     500             :     Options options,
     501             :     CancelToken cancelToken,
     502             :   }) {
     503           0 :     return requestUri<T>(
     504             :       uri,
     505             :       data: data,
     506           0 :       options: checkOptions('HEAD', options),
     507             :       cancelToken: cancelToken,
     508             :     );
     509             :   }
     510             : 
     511             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.request].
     512           1 :   @override
     513             :   Future<Response<T>> delete<T>(
     514             :     String path, {
     515             :     data,
     516             :     Map<String, dynamic> queryParameters,
     517             :     Options options,
     518             :     CancelToken cancelToken,
     519             :   }) {
     520           1 :     return request<T>(
     521             :       path,
     522             :       data: data,
     523             :       queryParameters: queryParameters,
     524           1 :       options: checkOptions('DELETE', options),
     525             :       cancelToken: cancelToken,
     526             :     );
     527             :   }
     528             : 
     529             :   /// Handy method to make http DELETE request, which is a alias of  [BaseDio.request].
     530           1 :   @override
     531             :   Future<Response<T>> deleteUri<T>(
     532             :     Uri uri, {
     533             :     data,
     534             :     Options options,
     535             :     CancelToken cancelToken,
     536             :   }) {
     537           1 :     return requestUri<T>(
     538             :       uri,
     539             :       data: data,
     540           1 :       options: checkOptions('DELETE', options),
     541             :       cancelToken: cancelToken,
     542             :     );
     543             :   }
     544             : 
     545             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.request].
     546           1 :   @override
     547             :   Future<Response<T>> patch<T>(
     548             :     String path, {
     549             :     data,
     550             :     Map<String, dynamic> queryParameters,
     551             :     Options options,
     552             :     CancelToken cancelToken,
     553             :     ProgressCallback onSendProgress,
     554             :     ProgressCallback onReceiveProgress,
     555             :   }) {
     556           1 :     return request<T>(
     557             :       path,
     558             :       data: data,
     559             :       queryParameters: queryParameters,
     560           1 :       options: checkOptions('PATCH', options),
     561             :       cancelToken: cancelToken,
     562             :       onSendProgress: onSendProgress,
     563             :       onReceiveProgress: onReceiveProgress,
     564             :     );
     565             :   }
     566             : 
     567             :   /// Handy method to make http PATCH request, which is a alias of  [BaseDio.request].
     568           1 :   @override
     569             :   Future<Response<T>> patchUri<T>(
     570             :     Uri uri, {
     571             :     data,
     572             :     Options options,
     573             :     CancelToken cancelToken,
     574             :     ProgressCallback onSendProgress,
     575             :     ProgressCallback onReceiveProgress,
     576             :   }) {
     577           1 :     return requestUri<T>(
     578             :       uri,
     579             :       data: data,
     580           1 :       options: checkOptions('PATCH', options),
     581             :       cancelToken: cancelToken,
     582             :       onSendProgress: onSendProgress,
     583             :       onReceiveProgress: onReceiveProgress,
     584             :     );
     585             :   }
     586             : 
     587             :   /// Assure the final future state is succeed!
     588           1 :   @override
     589             :   Future<Response<T>> resolve<T>(response) {
     590           1 :     if (response is! Future) {
     591           1 :       response = Future.value(response);
     592             :     }
     593           2 :     return response.then<Response<T>>((data) {
     594           1 :       return assureResponse<T>(data);
     595           0 :     }, onError: (err) {
     596             :       // transform 'error' to 'success'
     597           0 :       return assureResponse<T>(err);
     598             :     });
     599             :   }
     600             : 
     601             :   /// Assure the final future state is failed!
     602           1 :   @override
     603             :   Future<Response<T>> reject<T>(err) {
     604           1 :     if (err is! Future) {
     605           1 :       err = Future.error(err);
     606             :     }
     607           1 :     return err.then<Response<T>>((v) {
     608             :       // transform 'success' to 'error'
     609           0 :       throw assureDioError(v);
     610           1 :     }, onError: (e) {
     611           1 :       throw assureDioError(e);
     612             :     });
     613             :   }
     614             : 
     615             :   /// Lock the current Dio instance.
     616             :   ///
     617             :   /// Dio will enqueue the incoming request tasks instead
     618             :   /// send them directly when [interceptor.request] is locked.
     619           1 :   @override
     620             :   void lock() {
     621           3 :     interceptors.requestLock.lock();
     622             :   }
     623             : 
     624             :   /// Unlock the current Dio instance.
     625             :   ///
     626             :   /// Dio instance dequeue the request task。
     627           1 :   @override
     628             :   void unlock() {
     629           3 :     interceptors.requestLock.unlock();
     630             :   }
     631             : 
     632             :   ///Clear the current Dio instance waiting queue.
     633           0 :   @override
     634             :   void clear() {
     635           0 :     interceptors.requestLock.clear();
     636             :   }
     637             : 
     638             :   ///  Download the file and save it in local. The default http method is 'GET',
     639             :   ///  you can custom it by [Options.method].
     640             :   ///
     641             :   ///  [urlPath]: The file url.
     642             :   ///
     643             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     644             :   ///  a callback:
     645             :   ///  1. A path with String type, eg 'xs.jpg'
     646             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     647             :   ///  ```dart
     648             :   ///   await dio.download(url,(HttpHeaders responseHeaders){
     649             :   ///      ...
     650             :   ///      return '...';
     651             :   ///    });
     652             :   ///  ```
     653             :   ///
     654             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     655             :   ///  please refer to [ProgressCallback].
     656             :   ///
     657             :   /// [deleteOnError] Whether delete the file when error occurs. The default value is [true].
     658             :   ///
     659             :   ///  [lengthHeader] : The real size of original file (not compressed).
     660             :   ///  When file is compressed:
     661             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     662             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     663             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     664             :   ///
     665             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     666             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     667             :   ///
     668             :   ///     await dio.download(url, './example/flutter.svg',
     669             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}),  // disable gzip
     670             :   ///     onProgress: (received, total) {
     671             :   ///       if (total != -1) {
     672             :   ///        print((received / total * 100).toStringAsFixed(0) + '%');
     673             :   ///       }
     674             :   ///     });
     675             : 
     676             :   @override
     677           0 :   Future<Response> download(
     678             :     String urlPath,
     679             :     savePath, {
     680             :     ProgressCallback onReceiveProgress,
     681             :     Map<String, dynamic> queryParameters,
     682             :     CancelToken cancelToken,
     683             :     bool deleteOnError = true,
     684             :     String lengthHeader = Headers.contentLengthHeader,
     685             :     data,
     686             :     Options options,
     687             :   }) async {
     688           0 :     throw UnsupportedError('Unsupport download API in browser');
     689             :   }
     690             : 
     691             :   ///  Download the file and save it in local. The default http method is 'GET',
     692             :   ///  you can custom it by [Options.method].
     693             :   ///
     694             :   ///  [uri]: The file url.
     695             :   ///
     696             :   ///  [savePath]: The path to save the downloading file later. it can be a String or
     697             :   ///  a callback:
     698             :   ///  1. A path with String type, eg 'xs.jpg'
     699             :   ///  2. A callback `String Function(HttpHeaders responseHeaders)`; for example:
     700             :   ///  ```dart
     701             :   ///   await dio.downloadUri(uri,(HttpHeaders responseHeaders){
     702             :   ///      ...
     703             :   ///      return '...';
     704             :   ///    });
     705             :   ///  ```
     706             :   ///
     707             :   ///  [onReceiveProgress]: The callback to listen downloading progress.
     708             :   ///  please refer to [ProgressCallback].
     709             :   ///
     710             :   ///  [lengthHeader] : The real size of original file (not compressed).
     711             :   ///  When file is compressed:
     712             :   ///  1. If this value is 'content-length', the `total` argument of `onProgress` will be -1
     713             :   ///  2. If this value is not 'content-length', maybe a custom header indicates the original
     714             :   ///  file size , the `total` argument of `onProgress` will be this header value.
     715             :   ///
     716             :   ///  you can also disable the compression by specifying the 'accept-encoding' header value as '*'
     717             :   ///  to assure the value of `total` argument of `onProgress` is not -1. for example:
     718             :   ///
     719             :   ///     await dio.downloadUri(uri, './example/flutter.svg',
     720             :   ///     options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}),  // disable gzip
     721             :   ///     onProgress: (received, total) {
     722             :   ///       if (total != -1) {
     723             :   ///        print((received / total * 100).toStringAsFixed(0) + '%');
     724             :   ///       }
     725             :   ///     });
     726           0 :   @override
     727             :   Future<Response> downloadUri(
     728             :     Uri uri,
     729             :     savePath, {
     730             :     ProgressCallback onReceiveProgress,
     731             :     CancelToken cancelToken,
     732             :     bool deleteOnError = true,
     733             :     String lengthHeader = Headers.contentLengthHeader,
     734             :     data,
     735             :     Options options,
     736             :   }) {
     737           0 :     return download(
     738           0 :       uri.toString(),
     739             :       savePath,
     740             :       onReceiveProgress: onReceiveProgress,
     741             :       lengthHeader: lengthHeader,
     742             :       deleteOnError: deleteOnError,
     743             :       cancelToken: cancelToken,
     744             :       data: data,
     745             :       options: options,
     746             :     );
     747             :   }
     748             : 
     749             :   /// Make http request with options.
     750             :   ///
     751             :   /// [path] The url path.
     752             :   /// [data] The request data
     753             :   /// [options] The request options.
     754             :   @override
     755           5 :   Future<Response<T>> request<T>(
     756             :     String path, {
     757             :     data,
     758             :     Map<String, dynamic> queryParameters,
     759             :     CancelToken cancelToken,
     760             :     Options options,
     761             :     ProgressCallback onSendProgress,
     762             :     ProgressCallback onReceiveProgress,
     763             :   }) async {
     764           5 :     return _request<T>(
     765             :       path,
     766             :       data: data,
     767             :       queryParameters: queryParameters,
     768             :       cancelToken: cancelToken,
     769             :       options: options,
     770             :       onSendProgress: onSendProgress,
     771             :       onReceiveProgress: onReceiveProgress,
     772             :     );
     773             :   }
     774             : 
     775             :   /// Make http request with options.
     776             :   ///
     777             :   /// [uri] The uri.
     778             :   /// [data] The request data
     779             :   /// [options] The request options.
     780           1 :   @override
     781             :   Future<Response<T>> requestUri<T>(
     782             :     Uri uri, {
     783             :     data,
     784             :     CancelToken cancelToken,
     785             :     Options options,
     786             :     ProgressCallback onSendProgress,
     787             :     ProgressCallback onReceiveProgress,
     788             :   }) {
     789           1 :     return request(
     790           1 :       uri.toString(),
     791             :       data: data,
     792             :       cancelToken: cancelToken,
     793             :       options: options,
     794             :       onSendProgress: onSendProgress,
     795             :       onReceiveProgress: onReceiveProgress,
     796             :     );
     797             :   }
     798             : 
     799           5 :   Future<Response<T>> _request<T>(
     800             :     String path, {
     801             :     data,
     802             :     Map<String, dynamic> queryParameters,
     803             :     CancelToken cancelToken,
     804             :     Options options,
     805             :     ProgressCallback onSendProgress,
     806             :     ProgressCallback onReceiveProgress,
     807             :   }) async {
     808           5 :     if (_closed) {
     809           0 :       throw DioError(error: "Dio can't establish new connection after closed.");
     810             :     }
     811           0 :     options ??= Options();
     812           5 :     if (options is RequestOptions) {
     813           0 :       data = data ?? options.data;
     814           0 :       queryParameters = queryParameters ?? options.queryParameters;
     815           0 :       cancelToken = cancelToken ?? options.cancelToken;
     816           0 :       onSendProgress = onSendProgress ?? options.onSendProgress;
     817           0 :       onReceiveProgress = onReceiveProgress ?? options.onReceiveProgress;
     818             :     }
     819           5 :     var requestOptions = mergeOptions(options, path, data, queryParameters);
     820           5 :     requestOptions.onReceiveProgress = onReceiveProgress;
     821           5 :     requestOptions.onSendProgress = onSendProgress;
     822           5 :     requestOptions.cancelToken = cancelToken;
     823           5 :     if (T != dynamic &&
     824           4 :         !(requestOptions.responseType == ResponseType.bytes ||
     825           4 :             requestOptions.responseType == ResponseType.stream)) {
     826           1 :       if (T == String) {
     827           1 :         requestOptions.responseType = ResponseType.plain;
     828             :       } else {
     829           1 :         requestOptions.responseType = ResponseType.json;
     830             :       }
     831             :     }
     832             : 
     833          15 :     bool _isErrorOrException(t) => t is Exception || t is Error;
     834             : 
     835             :     // Convert the request/response interceptor to a functional callback in which
     836             :     // we can handle the return value of interceptor callback.
     837           5 :     Function _interceptorWrapper(interceptor, bool request) {
     838           5 :       return (data) async {
     839           7 :         var type = request ? (data is RequestOptions) : (data is Response);
     840             :         var lock =
     841          14 :             request ? interceptors.requestLock : interceptors.responseLock;
     842           5 :         if (_isErrorOrException(data) || type) {
     843           5 :           return listenCancelForAsyncTask(
     844             :             cancelToken,
     845          10 :             Future(() {
     846          10 :               return checkIfNeedEnqueue(lock, () {
     847             :                 if (type) {
     848           4 :                   if (!request) data.request = data.request ?? requestOptions;
     849          13 :                   return interceptor(data).then((e) => e ?? data);
     850             :                 } else {
     851           1 :                   throw assureDioError(data, requestOptions);
     852             :                 }
     853             :               });
     854             :             }),
     855             :           );
     856             :         } else {
     857           1 :           return assureResponse(data, requestOptions);
     858             :         }
     859             :       };
     860             :     }
     861             : 
     862             :     // Convert the error interceptor to a functional callback in which
     863             :     // we can handle the return value of interceptor callback.
     864           2 :     Function _errorInterceptorWrapper(errInterceptor) {
     865           2 :       return (err) async {
     866           2 :         if (err is! Response) {
     867           6 :           var _e = await errInterceptor(assureDioError(err, requestOptions));
     868           2 :           if (_e is! Response) {
     869           2 :             throw assureDioError(_e ?? err, requestOptions);
     870             :           }
     871             :           err = _e;
     872             :         }
     873             :         // err is a Response instance
     874             :         return err;
     875             :       };
     876             :     }
     877             : 
     878             :     // Build a request flow in which the processors(interceptors)
     879             :     // execute in FIFO order.
     880             : 
     881             :     // Start the request flow
     882             :     Future future;
     883           5 :     future = Future.value(requestOptions);
     884             :     // Add request interceptors to request flow
     885          12 :     interceptors.forEach((Interceptor interceptor) {
     886           6 :       future = future.then(_interceptorWrapper(interceptor.onRequest, true));
     887             :     });
     888             : 
     889             :     // Add dispatching callback to request flow
     890          15 :     future = future.then(_interceptorWrapper(_dispatchRequest, true));
     891             : 
     892             :     // Add response interceptors to request flow
     893          12 :     interceptors.forEach((Interceptor interceptor) {
     894           6 :       future = future.then(_interceptorWrapper(interceptor.onResponse, false));
     895             :     });
     896             : 
     897             :     // Add error handlers to request flow
     898          12 :     interceptors.forEach((Interceptor interceptor) {
     899           6 :       future = future.catchError(_errorInterceptorWrapper(interceptor.onError));
     900             :     });
     901             : 
     902             :     // Normalize errors, we convert error to the DioError
     903           8 :     return future.then<Response<T>>((data) {
     904           3 :       return assureResponse<T>(data);
     905          10 :     }).catchError((err) {
     906           5 :       if (err == null || _isErrorOrException(err)) {
     907           5 :         throw assureDioError(err, requestOptions);
     908             :       }
     909           0 :       return assureResponse<T>(err, requestOptions);
     910             :     });
     911             :   }
     912             : 
     913             :   // Initiate Http requests
     914           5 :   Future<Response<T>> _dispatchRequest<T>(RequestOptions options) async {
     915           5 :     var cancelToken = options.cancelToken;
     916             :     ResponseBody responseBody;
     917             :     try {
     918          10 :       var stream = await _transformData(options);
     919          15 :       responseBody = await httpClientAdapter.fetch(
     920             :         options,
     921             :         stream,
     922           2 :         cancelToken?.whenCancel,
     923             :       );
     924           7 :       responseBody.headers = responseBody.headers ?? {};
     925           6 :       var headers = Headers.fromMap(responseBody.headers ?? {});
     926           3 :       var ret = Response(
     927             :         headers: headers,
     928             :         request: options,
     929           4 :         redirects: responseBody.redirects ?? [],
     930           3 :         isRedirect: responseBody.isRedirect,
     931           3 :         statusCode: responseBody.statusCode,
     932           3 :         statusMessage: responseBody.statusMessage,
     933           3 :         extra: responseBody.extra,
     934             :       );
     935           6 :       var statusOk = options.validateStatus(responseBody.statusCode);
     936           3 :       if (statusOk || options.receiveDataWhenStatusError) {
     937           3 :         var forceConvert = !(T == dynamic || T == String) &&
     938           0 :             !(options.responseType == ResponseType.bytes ||
     939           0 :                 options.responseType == ResponseType.stream);
     940             :         String contentType;
     941             :         if (forceConvert) {
     942           0 :           contentType = headers.value(Headers.contentTypeHeader);
     943           0 :           headers.set(Headers.contentTypeHeader, Headers.jsonContentType);
     944             :         }
     945          12 :         ret.data = await transformer.transformResponse(options, responseBody);
     946             :         if (forceConvert) {
     947           0 :           headers.set(Headers.contentTypeHeader, contentType);
     948             :         }
     949             :       } else {
     950           4 :         await responseBody.stream.listen(null).cancel();
     951             :       }
     952           3 :       checkCancelled(cancelToken);
     953             :       if (statusOk) {
     954          12 :         return checkIfNeedEnqueue(interceptors.responseLock, () => ret);
     955             :       } else {
     956           3 :         throw DioError(
     957             :           response: ret,
     958           6 :           error: 'Http status error [${responseBody.statusCode}]',
     959             :           type: DioErrorType.RESPONSE,
     960             :         );
     961             :       }
     962             :     } catch (e) {
     963           5 :       throw assureDioError(e, options);
     964             :     }
     965             :   }
     966             : 
     967             :   // If the request has been cancelled, stop request and throw error.
     968           3 :   void checkCancelled(CancelToken cancelToken) {
     969           1 :     if (cancelToken != null && cancelToken.cancelError != null) {
     970           0 :       throw cancelToken.cancelError;
     971             :     }
     972             :   }
     973             : 
     974           5 :   Future<T> listenCancelForAsyncTask<T>(
     975             :       CancelToken cancelToken, Future<T> future) {
     976          10 :     return Future.any([
     977             :       if (cancelToken != null)
     978           8 :         cancelToken.whenCancel.then((e) => throw cancelToken.cancelError),
     979           5 :       future,
     980             :     ]);
     981             :   }
     982             : 
     983           5 :   Future<Stream<Uint8List>> _transformData(RequestOptions options) async {
     984           5 :     var data = options.data;
     985             :     List<int> bytes;
     986             :     Stream<List<int>> stream;
     987             :     if (data != null &&
     988           3 :         ['POST', 'PUT', 'PATCH', 'DELETE'].contains(options.method)) {
     989             :       // Handle the FormData
     990             :       int length;
     991           1 :       if (data is Stream) {
     992           0 :         assert(data is Stream<List>,
     993           0 :             'Stream type must be `Stream<List>`, but ${data.runtimeType} is found.');
     994             :         stream = data;
     995           0 :         options.headers.keys.any((String key) {
     996           0 :           if (key.toLowerCase() == Headers.contentLengthHeader) {
     997           0 :             length = int.parse(options.headers[key].toString());
     998             :             return true;
     999             :           }
    1000             :           return false;
    1001             :         });
    1002           1 :       } else if (data is FormData) {
    1003           0 :         if (data is FormData) {
    1004           0 :           options.headers[Headers.contentTypeHeader] =
    1005           0 :               'multipart/form-data; boundary=${data.boundary}';
    1006             :         }
    1007           0 :         stream = data.finalize();
    1008           0 :         length = data.length;
    1009             :       } else {
    1010             :         // Call request transformer.
    1011           3 :         var _data = await transformer.transformRequest(options);
    1012           1 :         if (options.requestEncoder != null) {
    1013           0 :           bytes = options.requestEncoder(_data, options);
    1014             :         } else {
    1015             :           //Default convert to utf8
    1016           1 :           bytes = utf8.encode(_data);
    1017             :         }
    1018             :         // support data sending progress
    1019           1 :         length = bytes.length;
    1020             : 
    1021           1 :         var group = <List<int>>[];
    1022             :         const size = 1024;
    1023           3 :         var groupCount = (bytes.length / size).ceil();
    1024           2 :         for (var i = 0; i < groupCount; ++i) {
    1025           1 :           var start = i * size;
    1026          16 :           group.add(bytes.sublist(start, math.min(start + size, bytes.length)));
    1027             :         }
    1028           1 :         stream = Stream.fromIterable(group);
    1029             :       }
    1030             : 
    1031             :       if (length != null) {
    1032           3 :         options.headers[Headers.contentLengthHeader] = length.toString();
    1033             :       }
    1034             :       var complete = 0;
    1035           2 :       var byteStream = stream.transform<Uint8List>(StreamTransformer.fromHandlers(
    1036           1 :         handleData: (data, sink) {
    1037           1 :           if (options.cancelToken != null && options.cancelToken.isCancelled) {
    1038             :             sink
    1039           0 :               ..addError(options.cancelToken.cancelError)
    1040           0 :               ..close();
    1041             :           } else {
    1042           2 :             sink.add(Uint8List.fromList(data));
    1043             :             if (length != null) {
    1044           2 :               complete += data.length;
    1045           1 :               if (options.onSendProgress != null) {
    1046           0 :                 options.onSendProgress(complete, length);
    1047             :               }
    1048             :             }
    1049             :           }
    1050             :         },
    1051             :       ));
    1052           2 :       if (options.sendTimeout > 0) {
    1053           0 :         byteStream.timeout(Duration(milliseconds: options.sendTimeout),
    1054           0 :             onTimeout: (sink) {
    1055           0 :           sink.addError(DioError(
    1056             :             request: options,
    1057           0 :             error: 'Sending timeout[${options.connectTimeout}ms]',
    1058             :             type: DioErrorType.SEND_TIMEOUT,
    1059             :           ));
    1060           0 :           sink.close();
    1061             :         });
    1062             :       }
    1063             :       return byteStream;
    1064             :     } else {
    1065          10 :       options.headers.remove(Headers.contentTypeHeader);
    1066             :     }
    1067             :     return null;
    1068             :   }
    1069             : 
    1070           5 :   RequestOptions mergeOptions(
    1071             :       Options opt, String url, data, Map<String, dynamic> queryParameters) {
    1072          20 :     var query = (Map<String, dynamic>.from(options.queryParameters ?? {}))
    1073          10 :       ..addAll(queryParameters ?? {});
    1074           5 :     final optBaseUrl = (opt is RequestOptions) ? opt.baseUrl : null;
    1075             :     final optConnectTimeout =
    1076           5 :         (opt is RequestOptions) ? opt.connectTimeout : null;
    1077           5 :     return RequestOptions(
    1078          10 :       method: (opt.method ?? options.method)?.toUpperCase() ?? 'GET',
    1079          25 :       headers: (Map.from(options.headers))..addAll(opt.headers),
    1080          10 :       baseUrl: optBaseUrl ?? options.baseUrl ?? '',
    1081             :       path: url,
    1082             :       data: data,
    1083          10 :       connectTimeout: optConnectTimeout ?? options.connectTimeout ?? 0,
    1084          15 :       sendTimeout: opt.sendTimeout ?? options.sendTimeout ?? 0,
    1085          15 :       receiveTimeout: opt.receiveTimeout ?? options.receiveTimeout ?? 0,
    1086             :       responseType:
    1087          13 :           opt.responseType ?? options.responseType ?? ResponseType.json,
    1088          25 :       extra: (Map.from(options.extra))..addAll(opt.extra),
    1089             :       contentType:
    1090          15 :           opt.contentType ?? options.contentType ?? Headers.jsonContentType,
    1091           5 :       validateStatus: opt.validateStatus ??
    1092          10 :           options.validateStatus ??
    1093           3 :           (int status) {
    1094           9 :             return status >= 200 && status < 300 || status == 304;
    1095             :           },
    1096           5 :       receiveDataWhenStatusError: opt.receiveDataWhenStatusError ??
    1097          10 :           options.receiveDataWhenStatusError ??
    1098             :           true,
    1099          15 :       followRedirects: opt.followRedirects ?? options.followRedirects ?? true,
    1100          15 :       maxRedirects: opt.maxRedirects ?? options.maxRedirects ?? 5,
    1101             :       queryParameters: query,
    1102          15 :       requestEncoder: opt.requestEncoder ?? options.requestEncoder,
    1103          15 :       responseDecoder: opt.responseDecoder ?? options.responseDecoder,
    1104             :     );
    1105             :   }
    1106             : 
    1107           5 :   Options checkOptions(method, options) {
    1108           5 :     options ??= Options();
    1109           5 :     options.method = method;
    1110             :     return options;
    1111             :   }
    1112             : 
    1113           5 :   FutureOr checkIfNeedEnqueue(Lock lock, EnqueueCallback callback) {
    1114           5 :     if (lock.locked) {
    1115           1 :       return lock.enqueue(callback);
    1116             :     } else {
    1117           5 :       return callback();
    1118             :     }
    1119             :   }
    1120             : 
    1121           5 :   DioError assureDioError(err, [RequestOptions requestOptions]) {
    1122             :     DioError dioError;
    1123           5 :     if (err is DioError) {
    1124             :       dioError = err;
    1125             :     } else {
    1126           3 :       dioError = DioError(error: err);
    1127             :     }
    1128          10 :     dioError.request = dioError.request ?? requestOptions;
    1129             :     return dioError;
    1130             :   }
    1131             : 
    1132           3 :   Response<T> assureResponse<T>(response, [RequestOptions requestOptions]) {
    1133           3 :     if (response is Response<T>) {
    1134           4 :       response.request = response.request ?? requestOptions;
    1135           3 :     } else if (response is! Response) {
    1136           1 :       response = Response<T>(data: response, request: requestOptions);
    1137             :     } else {
    1138           2 :       T data = response.data;
    1139           2 :       response = Response<T>(
    1140             :         data: data,
    1141           2 :         headers: response.headers,
    1142           2 :         request: response.request,
    1143           2 :         statusCode: response.statusCode,
    1144           2 :         isRedirect: response.isRedirect,
    1145           2 :         redirects: response.redirects,
    1146           2 :         statusMessage: response.statusMessage,
    1147             :       );
    1148             :     }
    1149             :     return response;
    1150             :   }
    1151             : }

Generated by: LCOV version 1.14