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 : }
|