From 219e93e5a84299d622c6164bc9effcbc8f84e8b0 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Thu, 28 Dec 2023 15:14:14 +0100 Subject: [PATCH 1/3] Replace DioMixin with DownloadAdapter --- dio/lib/browser.dart | 3 +- dio/lib/dio.dart | 6 +- dio/lib/io.dart | 3 +- dio/lib/src/{ => adapters}/adapter.dart | 8 +- dio/lib/src/adapters/browser_adapter.dart | 2 +- .../download/browser_download_adapter.dart | 29 + .../adapters/download/download_adapter.dart | 36 + .../download/io_download_adapter.dart} | 44 +- dio/lib/src/adapters/io_adapter.dart | 2 +- dio/lib/src/dio.dart | 600 +++++++++++++- dio/lib/src/dio/dio_for_browser.dart | 37 - dio/lib/src/dio_mixin.dart | 740 ------------------ dio/lib/src/interceptor.dart | 4 +- .../src/interceptors/imply_content_type.dart | 2 +- dio/lib/src/interceptors/log.dart | 2 +- dio/lib/src/options.dart | 2 +- .../src/transformers/sync_transformer.dart | 2 +- dio/test/dio_mixin_test.dart | 35 - dio/test/mock/http_mock.mocks.dart | 2 +- example/lib/extend_dio.dart | 2 +- 20 files changed, 674 insertions(+), 887 deletions(-) rename dio/lib/src/{ => adapters}/adapter.dart (95%) create mode 100644 dio/lib/src/adapters/download/browser_download_adapter.dart create mode 100644 dio/lib/src/adapters/download/download_adapter.dart rename dio/lib/src/{dio/dio_for_native.dart => adapters/download/io_download_adapter.dart} (81%) delete mode 100644 dio/lib/src/dio/dio_for_browser.dart delete mode 100644 dio/lib/src/dio_mixin.dart delete mode 100644 dio/test/dio_mixin_test.dart diff --git a/dio/lib/browser.dart b/dio/lib/browser.dart index 924c77457..7e1489669 100644 --- a/dio/lib/browser.dart +++ b/dio/lib/browser.dart @@ -1,2 +1,3 @@ export 'src/adapters/browser_adapter.dart' show BrowserHttpClientAdapter; -export 'src/dio/dio_for_browser.dart' show DioForBrowser; +export 'src/adapters/download/browser_download_adapter.dart' show BrowserDownloadAdapter; +export 'src/dio.dart' show Dio; diff --git a/dio/lib/dio.dart b/dio/lib/dio.dart index 60c9044c8..9fab3f900 100644 --- a/dio/lib/dio.dart +++ b/dio/lib/dio.dart @@ -5,11 +5,11 @@ /// {@category Plugins} library dio; -export 'src/adapter.dart'; +export 'src/adapters/adapter.dart'; +export 'src/adapters/download/download_adapter.dart'; export 'src/cancel_token.dart'; -export 'src/dio.dart'; +export 'src/dio.dart' hide InterceptorState, InterceptorResultType; export 'src/dio_exception.dart'; -export 'src/dio_mixin.dart' hide InterceptorState, InterceptorResultType; export 'src/form_data.dart'; export 'src/headers.dart'; export 'src/interceptors/log.dart'; diff --git a/dio/lib/io.dart b/dio/lib/io.dart index 69bffb16a..f9be52033 100644 --- a/dio/lib/io.dart +++ b/dio/lib/io.dart @@ -1,2 +1,3 @@ export 'src/adapters/io_adapter.dart' hide createAdapter; -export 'src/dio/dio_for_native.dart' show DioForNative; +export 'src/adapters/download/io_download_adapter.dart' hide createAdapter; +export 'src/dio.dart' show Dio; diff --git a/dio/lib/src/adapter.dart b/dio/lib/src/adapters/adapter.dart similarity index 95% rename from dio/lib/src/adapter.dart rename to dio/lib/src/adapters/adapter.dart index 7d3b589a3..2e6d4cbb4 100644 --- a/dio/lib/src/adapter.dart +++ b/dio/lib/src/adapters/adapter.dart @@ -1,11 +1,11 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'options.dart'; -import 'redirect_record.dart'; +import '../options.dart'; +import '../redirect_record.dart'; -import 'adapters/io_adapter.dart' - if (dart.library.html) 'adapters/browser_adapter.dart' as adapter; +import 'io_adapter.dart' + if (dart.library.html) 'browser_adapter.dart' as adapter; /// [HttpAdapter] is a bridge between [Dio] and [HttpClient]. /// diff --git a/dio/lib/src/adapters/browser_adapter.dart b/dio/lib/src/adapters/browser_adapter.dart index b388e42ec..10d56712c 100644 --- a/dio/lib/src/adapters/browser_adapter.dart +++ b/dio/lib/src/adapters/browser_adapter.dart @@ -5,7 +5,7 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; -import '../adapter.dart'; +import 'adapter.dart'; import '../dio_exception.dart'; import '../headers.dart'; import '../options.dart'; diff --git a/dio/lib/src/adapters/download/browser_download_adapter.dart b/dio/lib/src/adapters/download/browser_download_adapter.dart new file mode 100644 index 000000000..60e44fa12 --- /dev/null +++ b/dio/lib/src/adapters/download/browser_download_adapter.dart @@ -0,0 +1,29 @@ +import '../../cancel_token.dart'; +import '../../headers.dart'; +import '../../options.dart'; +import '../../response.dart'; +import '../../transformer.dart'; +import 'download_adapter.dart'; + +DownloadAdapter createAdapter() => BrowserDownloadAdapter(); + +class BrowserDownloadAdapter implements DownloadAdapter { + @override + Future download( + String urlPath, + dynamic savePath, { + required RequestCallback request, + required Transformer transformer, + ProgressCallback? onReceiveProgress, + Map? queryParameters, + CancelToken? cancelToken, + bool deleteOnError = true, + String lengthHeader = Headers.contentLengthHeader, + Object? data, + Options? options, + }) { + throw UnsupportedError( + 'The download method is not available in the Web environment.', + ); + } +} diff --git a/dio/lib/src/adapters/download/download_adapter.dart b/dio/lib/src/adapters/download/download_adapter.dart new file mode 100644 index 000000000..46026b41a --- /dev/null +++ b/dio/lib/src/adapters/download/download_adapter.dart @@ -0,0 +1,36 @@ +import '../../cancel_token.dart'; +import '../../headers.dart'; +import '../../options.dart'; +import '../../response.dart'; +import '../../transformer.dart'; + +import 'io_download_adapter.dart' + if (dart.library.html) 'browser_download_adapter.dart' as adapter; + +typedef RequestCallback = Future> Function( + String url, { + Object? data, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, +}); + +abstract class DownloadAdapter { + factory DownloadAdapter() => adapter.createAdapter(); + + Future download( + String urlPath, + dynamic savePath, { + required RequestCallback request, + required Transformer transformer, + ProgressCallback? onReceiveProgress, + Map? queryParameters, + CancelToken? cancelToken, + bool deleteOnError = true, + String lengthHeader = Headers.contentLengthHeader, + Object? data, + Options? options, + }); +} diff --git a/dio/lib/src/dio/dio_for_native.dart b/dio/lib/src/adapters/download/io_download_adapter.dart similarity index 81% rename from dio/lib/src/dio/dio_for_native.dart rename to dio/lib/src/adapters/download/io_download_adapter.dart index 7857305bc..0a2ad2bfe 100644 --- a/dio/lib/src/dio/dio_for_native.dart +++ b/dio/lib/src/adapters/download/io_download_adapter.dart @@ -1,33 +1,25 @@ import 'dart:async'; import 'dart:io'; +import '../../dio.dart'; import '../adapter.dart'; -import '../cancel_token.dart'; -import '../dio_exception.dart'; -import '../dio_mixin.dart'; -import '../response.dart'; -import '../dio.dart'; -import '../headers.dart'; -import '../options.dart'; -import '../adapters/io_adapter.dart'; +import '../../cancel_token.dart'; +import '../../dio_exception.dart'; +import '../../headers.dart'; +import '../../options.dart'; +import '../../response.dart'; +import '../../transformer.dart'; +import 'download_adapter.dart'; -/// Create the [Dio] instance for native platforms. -Dio createDio([BaseOptions? baseOptions]) => DioForNative(baseOptions); +DownloadAdapter createAdapter() => IODownloadAdapter(); -/// Implements features for [Dio] on native platforms. -class DioForNative extends DioMixin implements Dio { - /// Create Dio instance with default [BaseOptions]. - /// It is recommended that an application use only the same DIO singleton. - DioForNative([BaseOptions? baseOptions]) { - options = baseOptions ?? BaseOptions(); - httpClientAdapter = IOHttpClientAdapter(); - } - - /// {@macro dio.Dio.download} +class IODownloadAdapter implements DownloadAdapter { @override Future download( String urlPath, dynamic savePath, { + required RequestCallback request, + required Transformer transformer, ProgressCallback? onReceiveProgress, Map? queryParameters, CancelToken? cancelToken, @@ -36,14 +28,14 @@ class DioForNative extends DioMixin implements Dio { Object? data, Options? options, }) async { - options ??= DioMixin.checkOptions('GET', options); + options ??= Dio.checkOptions('GET', options); // Manually set the `responseType` to [ResponseType.stream] // to retrieve the response stream. // Do not modify previous options. options = options.copyWith(responseType: ResponseType.stream); final Response response; try { - response = await request( + response = await request( urlPath, data: data, options: options, @@ -153,7 +145,7 @@ class DioForNative extends DioMixin implements Dio { } } finally { completer.completeError( - DioMixin.assureDioException(e, response.requestOptions), + Dio.assureDioException(e, response.requestOptions), ); } }); @@ -166,7 +158,7 @@ class DioForNative extends DioMixin implements Dio { completer.complete(response); } catch (e) { completer.completeError( - DioMixin.assureDioException(e, response.requestOptions), + Dio.assureDioException(e, response.requestOptions), ); } }, @@ -175,7 +167,7 @@ class DioForNative extends DioMixin implements Dio { await closeAndDelete(); } finally { completer.completeError( - DioMixin.assureDioException(e, response.requestOptions), + Dio.assureDioException(e, response.requestOptions), ); } }, @@ -185,6 +177,6 @@ class DioForNative extends DioMixin implements Dio { await subscription.cancel(); await closeAndDelete(); }); - return DioMixin.listenCancelForAsyncTask(cancelToken, completer.future); + return Dio.listenCancelForAsyncTask(cancelToken, completer.future); } } diff --git a/dio/lib/src/adapters/io_adapter.dart b/dio/lib/src/adapters/io_adapter.dart index eb70b3bc8..a83ef22b7 100644 --- a/dio/lib/src/adapters/io_adapter.dart +++ b/dio/lib/src/adapters/io_adapter.dart @@ -4,7 +4,7 @@ import 'dart:typed_data'; import 'package:async/async.dart'; -import '../adapter.dart'; +import 'adapter.dart'; import '../dio_exception.dart'; import '../options.dart'; import '../redirect_record.dart'; diff --git a/dio/lib/src/dio.dart b/dio/lib/src/dio.dart index e0a13f3fd..6fb3fbb5e 100644 --- a/dio/lib/src/dio.dart +++ b/dio/lib/src/dio.dart @@ -1,15 +1,25 @@ import 'dart:async'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:math' as math; +import 'dart:typed_data'; -import 'adapter.dart'; -import 'dio_mixin.dart'; +import 'package:dio/src/adapters/download/download_adapter.dart'; +import 'package:meta/meta.dart'; + +import 'adapters/adapter.dart'; +import 'dio_exception.dart'; +import 'form_data.dart'; +import 'interceptors/imply_content_type.dart'; import 'options.dart'; import 'headers.dart'; import 'cancel_token.dart'; -import 'transformer.dart'; +import 'progress_stream/io_progress_stream.dart'; import 'response.dart'; +import 'transformer.dart'; +import 'transformers/background_transformer.dart'; -import 'dio/dio_for_native.dart' - if (dart.library.html) 'dio/dio_for_browser.dart'; +part 'interceptor.dart'; /// Dio enables you to make HTTP requests easily. /// @@ -34,24 +44,29 @@ import 'dio/dio_for_native.dart' /// dio.options.connectTimeout = const Duration(seconds: 5); /// dio.options.receiveTimeout = const Duration(seconds: 5); /// ``` -abstract class Dio { +class Dio { /// Create the default [Dio] instance with the default implementation /// based on different platforms. - factory Dio([BaseOptions? options]) => createDio(options); + Dio([BaseOptions? options]) : options = options ?? BaseOptions(); /// Default Request config. More see [BaseOptions] . - late BaseOptions options; + BaseOptions options; /// Return the interceptors added into the instance. - Interceptors get interceptors; + final Interceptors interceptors = Interceptors(); /// The adapter that the instance is using. - late HttpClientAdapter httpClientAdapter; + HttpClientAdapter httpClientAdapter = HttpClientAdapter(); + + /// The download adapter that the instance is using. + final DownloadAdapter downloadAdapter = DownloadAdapter(); /// [Transformer] allows changes to the request/response data before it is /// sent/received to/from the server. /// This is only applicable for requests that have payload. - late Transformer transformer; + Transformer transformer = BackgroundTransformer(); + + bool _closed = false; /// Shuts down the dio client. /// @@ -61,7 +76,10 @@ abstract class Dio { /// closed connections will receive an error event to indicate that the client /// was shut down. In both cases trying to establish a new connection after /// calling [close] will throw an exception. - void close({bool force = false}); + void close({bool force = false}) { + _closed = true; + httpClientAdapter.close(force: force); + } /// Convenience method to make an HTTP HEAD request. Future> head( @@ -70,7 +88,15 @@ abstract class Dio { Map? queryParameters, Options? options, CancelToken? cancelToken, - }); + }) { + return request( + path, + data: data, + queryParameters: queryParameters, + options: checkOptions('HEAD', options), + cancelToken: cancelToken, + ); + } /// Convenience method to make an HTTP HEAD request with [Uri]. Future> headUri( @@ -78,17 +104,33 @@ abstract class Dio { Object? data, Options? options, CancelToken? cancelToken, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('HEAD', options), + cancelToken: cancelToken, + ); + } /// Convenience method to make an HTTP GET request. Future> get( String path, { - Object? data, Map? queryParameters, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, - }); + }) { + return request( + path, + data: data, + queryParameters: queryParameters, + options: checkOptions('GET', options), + onReceiveProgress: onReceiveProgress, + cancelToken: cancelToken, + ); + } /// Convenience method to make an HTTP GET request with [Uri]. Future> getUri( @@ -97,7 +139,15 @@ abstract class Dio { Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('GET', options), + onReceiveProgress: onReceiveProgress, + cancelToken: cancelToken, + ); + } /// Convenience method to make an HTTP POST request. Future> post( @@ -108,7 +158,17 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return request( + path, + data: data, + options: checkOptions('POST', options), + queryParameters: queryParameters, + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP POST request with [Uri]. Future> postUri( @@ -118,7 +178,16 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('POST', options), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP PUT request. Future> put( @@ -129,7 +198,17 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return request( + path, + data: data, + queryParameters: queryParameters, + options: checkOptions('PUT', options), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP PUT request with [Uri]. Future> putUri( @@ -139,7 +218,16 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('PUT', options), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP PATCH request. Future> patch( @@ -150,7 +238,17 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return request( + path, + data: data, + queryParameters: queryParameters, + options: checkOptions('PATCH', options), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP PATCH request with [Uri]. Future> patchUri( @@ -160,7 +258,16 @@ abstract class Dio { CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('PATCH', options), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// Convenience method to make an HTTP DELETE request. Future> delete( @@ -169,7 +276,15 @@ abstract class Dio { Map? queryParameters, Options? options, CancelToken? cancelToken, - }); + }) { + return request( + path, + data: data, + queryParameters: queryParameters, + options: checkOptions('DELETE', options), + cancelToken: cancelToken, + ); + } /// Convenience method to make an HTTP DELETE request with [Uri]. Future> deleteUri( @@ -177,7 +292,14 @@ abstract class Dio { Object? data, Options? options, CancelToken? cancelToken, - }); + }) { + return requestUri( + uri, + data: data, + options: checkOptions('DELETE', options), + cancelToken: cancelToken, + ); + } /// {@template dio.Dio.download} /// Download the file and save it in local. The default http method is "GET", @@ -245,7 +367,20 @@ abstract class Dio { String lengthHeader = Headers.contentLengthHeader, Object? data, Options? options, - }); + }) => + downloadAdapter.download( + urlPath, + savePath, + request: request, + transformer: transformer, + onReceiveProgress: onReceiveProgress, + queryParameters: queryParameters, + cancelToken: cancelToken, + deleteOnError: deleteOnError, + lengthHeader: lengthHeader, + data: data, + options: options, + ); /// {@macro dio.Dio.download} Future downloadUri( @@ -272,14 +407,34 @@ abstract class Dio { /// Make HTTP request with options. Future> request( - String url, { + String path, { Object? data, Map? queryParameters, CancelToken? cancelToken, Options? options, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) async { + final requestOptions = (options ?? Options()).compose( + this.options, + path, + data: data, + queryParameters: queryParameters, + onReceiveProgress: onReceiveProgress, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + sourceStackTrace: StackTrace.current, + ); + + if (_closed) { + throw DioException.connectionError( + reason: "Dio can't establish a new connection after it was closed.", + requestOptions: requestOptions, + ); + } + + return fetch(requestOptions); + } /// Make http request with options with [Uri]. Future> requestUri( @@ -289,9 +444,394 @@ abstract class Dio { Options? options, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, - }); + }) { + return request( + uri.toString(), + data: data, + cancelToken: cancelToken, + options: options, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } /// The eventual method to submit requests. All callers for requests should /// eventually go through this method. - Future> fetch(RequestOptions requestOptions); + Future> fetch(RequestOptions requestOptions) async { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + + // Convert the request interceptor to a functional callback in which + // we can handle the return value of interceptor callback. + FutureOr Function(dynamic) requestInterceptorWrapper( + InterceptorSendCallback interceptor, + ) { + return (dynamic incomingState) async { + final state = incomingState as InterceptorState; + if (state.type == InterceptorResultType.next) { + return listenCancelForAsyncTask( + requestOptions.cancelToken, + Future(() { + final requestHandler = RequestInterceptorHandler(); + interceptor(state.data as RequestOptions, requestHandler); + return requestHandler.future; + }), + ); + } else { + return state; + } + }; + } + + // Convert the response interceptor to a functional callback in which + // we can handle the return value of interceptor callback. + FutureOr Function(dynamic) responseInterceptorWrapper( + InterceptorSuccessCallback interceptor, + ) { + return (dynamic incomingState) async { + final state = incomingState as InterceptorState; + if (state.type == InterceptorResultType.next || + state.type == InterceptorResultType.resolveCallFollowing) { + return listenCancelForAsyncTask( + requestOptions.cancelToken, + Future(() { + final responseHandler = ResponseInterceptorHandler(); + interceptor(state.data as Response, responseHandler); + return responseHandler.future; + }), + ); + } else { + return state; + } + }; + } + + // Convert the error interceptor to a functional callback in which + // we can handle the return value of interceptor callback. + FutureOr Function(Object) errorInterceptorWrapper( + InterceptorErrorCallback interceptor, + ) { + return (error) { + final state = error is InterceptorState + ? error + : InterceptorState(assureDioException(error, requestOptions)); + Future handleError() async { + final errorHandler = ErrorInterceptorHandler(); + interceptor(state.data, errorHandler); + return errorHandler.future; + } + + // The request has already been cancelled, + // there is no need to listen for another cancellation. + if (state.data is DioException && + state.data.type == DioExceptionType.cancel) { + return handleError(); + } else if (state.type == InterceptorResultType.next || + state.type == InterceptorResultType.rejectCallFollowing) { + return listenCancelForAsyncTask( + requestOptions.cancelToken, + Future(handleError), + ); + } else { + throw error; + } + }; + } + + // Build a request flow in which the processors(interceptors) + // execute in FIFO order. + Future future = Future( + () => InterceptorState(requestOptions), + ); + + // Add request interceptors into the request flow. + for (final interceptor in interceptors) { + final fun = interceptor is QueuedInterceptor + ? interceptor._handleRequest + : interceptor.onRequest; + future = future.then(requestInterceptorWrapper(fun)); + } + + // Add dispatching callback into the request flow. + future = future.then( + requestInterceptorWrapper(( + RequestOptions reqOpt, + RequestInterceptorHandler handler, + ) { + requestOptions = reqOpt; + _dispatchRequest(reqOpt) + .then((value) => handler.resolve(value, true)) + .catchError((e) { + handler.reject(e as DioException, true); + }); + }), + ); + + // Add response interceptors into the request flow + for (final interceptor in interceptors) { + final fun = interceptor is QueuedInterceptor + ? interceptor._handleResponse + : interceptor.onResponse; + future = future.then(responseInterceptorWrapper(fun)); + } + + // Add error handlers into the request flow. + for (final interceptor in interceptors) { + final fun = interceptor is QueuedInterceptor + ? interceptor._handleError + : interceptor.onError; + future = future.catchError(errorInterceptorWrapper(fun)); + } + // Normalize errors, converts errors to [DioException]. + return future.then>((data) { + return assureResponse( + data is InterceptorState ? data.data : data, + requestOptions, + ); + }).catchError((Object e) { + final isState = e is InterceptorState; + if (isState) { + if (e.type == InterceptorResultType.resolve) { + return assureResponse(e.data, requestOptions); + } + } + throw assureDioException(isState ? e.data : e, requestOptions); + }); + } + + Future> _dispatchRequest(RequestOptions reqOpt) async { + final cancelToken = reqOpt.cancelToken; + try { + final stream = await _transformData(reqOpt); + final responseBody = await httpClientAdapter.fetch( + reqOpt, + stream, + cancelToken?.whenCancel, + ); + final headers = Headers.fromMap( + responseBody.headers, + preserveHeaderCase: reqOpt.preserveHeaderCase, + ); + // Make sure headers and [ResponseBody.headers] are the same instance. + responseBody.headers = headers.map; + final ret = Response( + data: null, + headers: headers, + requestOptions: reqOpt, + redirects: responseBody.redirects ?? [], + isRedirect: responseBody.isRedirect, + statusCode: responseBody.statusCode, + statusMessage: responseBody.statusMessage, + extra: responseBody.extra, + ); + final statusOk = reqOpt.validateStatus(responseBody.statusCode); + if (statusOk || reqOpt.receiveDataWhenStatusError == true) { + Object? data = await transformer.transformResponse( + reqOpt, + responseBody, + ); + // Make the response as null before returned as JSON. + if (data is String && + data.isEmpty && + T != dynamic && + T != String && + reqOpt.responseType == ResponseType.json) { + data = null; + } + ret.data = data; + } else { + await responseBody.stream.listen(null).cancel(); + } + checkCancelled(cancelToken); + if (statusOk) { + return ret; + } else { + throw DioException.badResponse( + statusCode: responseBody.statusCode, + requestOptions: reqOpt, + response: ret, + ); + } + } catch (e) { + throw assureDioException(e, reqOpt); + } + } + + bool _isValidToken(String token) { + // from https://www.rfc-editor.org/rfc/rfc2616#page-15 + // + // CTL = + // separators = "(" | ")" | "<" | ">" | "@" + // | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" + // | "{" | "}" | SP | HT + // token = 1* + const String validChars = r' ' + r" ! #$%&' *+ -. 0123456789 " + r' ABCDEFGHIJKLMNOPQRSTUVWXYZ ^_' + r'`abcdefghijklmnopqrstuvwxyz | ~ '; + for (final int codeUnit in token.codeUnits) { + if (codeUnit >= validChars.length || + validChars.codeUnitAt(codeUnit) == 0x20) { + return false; + } + } + return true; + } + + Future?> _transformData(RequestOptions options) async { + if (!_isValidToken(options.method)) { + throw ArgumentError.value(options.method, 'method'); + } + final data = options.data; + if (data != null) { + final Stream> stream; + // Handle the FormData. + int? length; + if (data is Stream) { + if (data is! Stream>) { + throw ArgumentError.value( + data.runtimeType, + 'data', + 'Stream type must be `Stream>`', + ); + } + stream = data; + options.headers.keys.any((String key) { + if (key.toLowerCase() == Headers.contentLengthHeader) { + length = int.parse(options.headers[key].toString()); + return true; + } + return false; + }); + } else if (data is FormData) { + options.headers[Headers.contentTypeHeader] = + '${Headers.multipartFormDataContentType}; ' + 'boundary=${data.boundary}'; + stream = data.finalize(); + length = data.length; + options.headers[Headers.contentLengthHeader] = length.toString(); + } else { + final List bytes; + if (data is Uint8List) { + // Handle binary data which does not need to be transformed. + bytes = data; + } else { + // Call the request transformer. + final transformed = await transformer.transformRequest(options); + if (options.requestEncoder != null) { + final encoded = options.requestEncoder!(transformed, options); + + if (encoded is Future) { + bytes = await encoded; + } else { + bytes = encoded; + } + } else { + // Converts the data to UTF-8 by default. + bytes = utf8.encode(transformed); + } + } + + // Allocate send progress. + length = bytes.length; + options.headers[Headers.contentLengthHeader] = length.toString(); + + final group = >[]; + const size = 1024; + final groupCount = (bytes.length / size).ceil(); + for (int i = 0; i < groupCount; ++i) { + final start = i * size; + group.add(bytes.sublist(start, math.min(start + size, bytes.length))); + } + stream = Stream.fromIterable(group); + } + return addProgress(stream, length, options); + } + return null; + } + + // If the request has been cancelled, stop the request and throw error. + @internal + static void checkCancelled(CancelToken? cancelToken) { + final error = cancelToken?.cancelError; + if (error != null) { + throw error; + } + } + + @internal + static Future listenCancelForAsyncTask( + CancelToken? cancelToken, + Future future, + ) { + return Future.any([ + if (cancelToken != null) cancelToken.whenCancel.then((e) => throw e), + future, + ]); + } + + @internal + static Options checkOptions(String method, Options? options) { + options ??= Options(); + options.method = method; + return options; + } + + @internal + static DioException assureDioException( + Object error, + RequestOptions requestOptions, + ) { + if (error is DioException) { + return error; + } + return DioException( + requestOptions: requestOptions, + error: error, + ); + } + + @internal + static Response assureResponse( + Object response, + RequestOptions requestOptions, + ) { + if (response is! Response) { + return Response( + data: response as T, + requestOptions: requestOptions, + ); + } else if (response is! Response) { + final T data = response.data as T; + final Headers headers; + if (data is ResponseBody) { + headers = Headers.fromMap( + data.headers, + preserveHeaderCase: requestOptions.preserveHeaderCase, + ); + } else { + headers = response.headers; + } + return Response( + data: data, + headers: headers, + requestOptions: response.requestOptions, + statusCode: response.statusCode, + isRedirect: response.isRedirect, + redirects: response.redirects, + statusMessage: response.statusMessage, + extra: response.extra, + ); + } + return response; + } } diff --git a/dio/lib/src/dio/dio_for_browser.dart b/dio/lib/src/dio/dio_for_browser.dart deleted file mode 100644 index 8367446bc..000000000 --- a/dio/lib/src/dio/dio_for_browser.dart +++ /dev/null @@ -1,37 +0,0 @@ -import '../adapters/browser_adapter.dart'; -import '../cancel_token.dart'; -import '../dio.dart'; -import '../dio_mixin.dart'; -import '../headers.dart'; -import '../options.dart'; -import '../response.dart'; - -/// Create the [Dio] instance for Web platforms. -Dio createDio([BaseOptions? options]) => DioForBrowser(options); - -/// Implements features for [Dio] on Web platforms. -class DioForBrowser extends DioMixin implements Dio { - /// Create Dio instance with default [Options]. - /// It's mostly just one Dio instance in your application. - DioForBrowser([BaseOptions? options]) { - this.options = options ?? BaseOptions(); - httpClientAdapter = BrowserHttpClientAdapter(); - } - - @override - Future download( - String urlPath, - dynamic savePath, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError = true, - String lengthHeader = Headers.contentLengthHeader, - Object? data, - Options? options, - }) { - throw UnsupportedError( - 'The download method is not available in the Web environment.', - ); - } -} diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart deleted file mode 100644 index de3f21752..000000000 --- a/dio/lib/src/dio_mixin.dart +++ /dev/null @@ -1,740 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; -import 'dart:convert'; -import 'dart:math' as math; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -import 'adapter.dart'; -import 'cancel_token.dart'; -import 'dio.dart'; -import 'dio_exception.dart'; -import 'form_data.dart'; -import 'headers.dart'; -import 'interceptors/imply_content_type.dart'; -import 'options.dart'; -import 'response.dart'; -import 'transformer.dart'; -import 'transformers/background_transformer.dart'; - -import 'progress_stream/io_progress_stream.dart' - if (dart.library.html) 'progress_stream/browser_progress_stream.dart'; - -part 'interceptor.dart'; - -// TODO(EVERYONE): Use `mixin class` when the lower bound of SDK is raised to 3.0.0. -abstract class DioMixin implements Dio { - /// The base request config for the instance. - @override - late BaseOptions options; - - /// Each Dio instance has a interceptor group by which you can - /// intercept requests or responses before they are ended. - @override - Interceptors get interceptors => _interceptors; - final Interceptors _interceptors = Interceptors(); - - @override - late HttpClientAdapter httpClientAdapter; - - /// The default [Transformer] that transfers requests and responses - /// into corresponding content to send. - @override - Transformer transformer = BackgroundTransformer(); - - bool _closed = false; - - @override - void close({bool force = false}) { - _closed = true; - httpClientAdapter.close(force: force); - } - - @override - Future> get( - String path, { - Map? queryParameters, - Object? data, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onReceiveProgress, - }) { - return request( - path, - data: data, - queryParameters: queryParameters, - options: checkOptions('GET', options), - onReceiveProgress: onReceiveProgress, - cancelToken: cancelToken, - ); - } - - @override - Future> getUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onReceiveProgress, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('GET', options), - onReceiveProgress: onReceiveProgress, - cancelToken: cancelToken, - ); - } - - @override - Future> post( - String path, { - Object? data, - Map? queryParameters, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return request( - path, - data: data, - options: checkOptions('POST', options), - queryParameters: queryParameters, - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> postUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('POST', options), - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> put( - String path, { - Object? data, - Map? queryParameters, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return request( - path, - data: data, - queryParameters: queryParameters, - options: checkOptions('PUT', options), - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> putUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('PUT', options), - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> head( - String path, { - Object? data, - Map? queryParameters, - Options? options, - CancelToken? cancelToken, - }) { - return request( - path, - data: data, - queryParameters: queryParameters, - options: checkOptions('HEAD', options), - cancelToken: cancelToken, - ); - } - - @override - Future> headUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('HEAD', options), - cancelToken: cancelToken, - ); - } - - @override - Future> delete( - String path, { - Object? data, - Map? queryParameters, - Options? options, - CancelToken? cancelToken, - }) { - return request( - path, - data: data, - queryParameters: queryParameters, - options: checkOptions('DELETE', options), - cancelToken: cancelToken, - ); - } - - @override - Future> deleteUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('DELETE', options), - cancelToken: cancelToken, - ); - } - - @override - Future> patch( - String path, { - Object? data, - Map? queryParameters, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return request( - path, - data: data, - queryParameters: queryParameters, - options: checkOptions('PATCH', options), - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> patchUri( - Uri uri, { - Object? data, - Options? options, - CancelToken? cancelToken, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return requestUri( - uri, - data: data, - options: checkOptions('PATCH', options), - cancelToken: cancelToken, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future downloadUri( - Uri uri, - dynamic savePath, { - ProgressCallback? onReceiveProgress, - CancelToken? cancelToken, - bool deleteOnError = true, - String lengthHeader = Headers.contentLengthHeader, - Object? data, - Options? options, - }) { - return download( - uri.toString(), - savePath, - onReceiveProgress: onReceiveProgress, - lengthHeader: lengthHeader, - deleteOnError: deleteOnError, - cancelToken: cancelToken, - data: data, - options: options, - ); - } - - @override - Future download( - String urlPath, - dynamic savePath, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError = true, - String lengthHeader = Headers.contentLengthHeader, - Object? data, - Options? options, - }) { - throw UnimplementedError(); - } - - @override - Future> requestUri( - Uri uri, { - Object? data, - CancelToken? cancelToken, - Options? options, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) { - return request( - uri.toString(), - data: data, - cancelToken: cancelToken, - options: options, - onSendProgress: onSendProgress, - onReceiveProgress: onReceiveProgress, - ); - } - - @override - Future> request( - String path, { - Object? data, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - }) async { - final requestOptions = (options ?? Options()).compose( - this.options, - path, - data: data, - queryParameters: queryParameters, - onReceiveProgress: onReceiveProgress, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - sourceStackTrace: StackTrace.current, - ); - - if (_closed) { - throw DioException.connectionError( - reason: "Dio can't establish a new connection after it was closed.", - requestOptions: requestOptions, - ); - } - - return fetch(requestOptions); - } - - @override - Future> fetch(RequestOptions requestOptions) async { - if (T != dynamic && - !(requestOptions.responseType == ResponseType.bytes || - requestOptions.responseType == ResponseType.stream)) { - if (T == String) { - requestOptions.responseType = ResponseType.plain; - } else { - requestOptions.responseType = ResponseType.json; - } - } - - // Convert the request interceptor to a functional callback in which - // we can handle the return value of interceptor callback. - FutureOr Function(dynamic) requestInterceptorWrapper( - InterceptorSendCallback interceptor, - ) { - return (dynamic incomingState) async { - final state = incomingState as InterceptorState; - if (state.type == InterceptorResultType.next) { - return listenCancelForAsyncTask( - requestOptions.cancelToken, - Future(() { - final requestHandler = RequestInterceptorHandler(); - interceptor(state.data as RequestOptions, requestHandler); - return requestHandler.future; - }), - ); - } else { - return state; - } - }; - } - - // Convert the response interceptor to a functional callback in which - // we can handle the return value of interceptor callback. - FutureOr Function(dynamic) responseInterceptorWrapper( - InterceptorSuccessCallback interceptor, - ) { - return (dynamic incomingState) async { - final state = incomingState as InterceptorState; - if (state.type == InterceptorResultType.next || - state.type == InterceptorResultType.resolveCallFollowing) { - return listenCancelForAsyncTask( - requestOptions.cancelToken, - Future(() { - final responseHandler = ResponseInterceptorHandler(); - interceptor(state.data as Response, responseHandler); - return responseHandler.future; - }), - ); - } else { - return state; - } - }; - } - - // Convert the error interceptor to a functional callback in which - // we can handle the return value of interceptor callback. - FutureOr Function(Object) errorInterceptorWrapper( - InterceptorErrorCallback interceptor, - ) { - return (error) { - final state = error is InterceptorState - ? error - : InterceptorState(assureDioException(error, requestOptions)); - Future handleError() async { - final errorHandler = ErrorInterceptorHandler(); - interceptor(state.data, errorHandler); - return errorHandler.future; - } - - // The request has already been cancelled, - // there is no need to listen for another cancellation. - if (state.data is DioException && - state.data.type == DioExceptionType.cancel) { - return handleError(); - } else if (state.type == InterceptorResultType.next || - state.type == InterceptorResultType.rejectCallFollowing) { - return listenCancelForAsyncTask( - requestOptions.cancelToken, - Future(handleError), - ); - } else { - throw error; - } - }; - } - - // Build a request flow in which the processors(interceptors) - // execute in FIFO order. - Future future = Future( - () => InterceptorState(requestOptions), - ); - - // Add request interceptors into the request flow. - for (final interceptor in interceptors) { - final fun = interceptor is QueuedInterceptor - ? interceptor._handleRequest - : interceptor.onRequest; - future = future.then(requestInterceptorWrapper(fun)); - } - - // Add dispatching callback into the request flow. - future = future.then( - requestInterceptorWrapper(( - RequestOptions reqOpt, - RequestInterceptorHandler handler, - ) { - requestOptions = reqOpt; - _dispatchRequest(reqOpt) - .then((value) => handler.resolve(value, true)) - .catchError((e) { - handler.reject(e as DioException, true); - }); - }), - ); - - // Add response interceptors into the request flow - for (final interceptor in interceptors) { - final fun = interceptor is QueuedInterceptor - ? interceptor._handleResponse - : interceptor.onResponse; - future = future.then(responseInterceptorWrapper(fun)); - } - - // Add error handlers into the request flow. - for (final interceptor in interceptors) { - final fun = interceptor is QueuedInterceptor - ? interceptor._handleError - : interceptor.onError; - future = future.catchError(errorInterceptorWrapper(fun)); - } - // Normalize errors, converts errors to [DioException]. - return future.then>((data) { - return assureResponse( - data is InterceptorState ? data.data : data, - requestOptions, - ); - }).catchError((Object e) { - final isState = e is InterceptorState; - if (isState) { - if (e.type == InterceptorResultType.resolve) { - return assureResponse(e.data, requestOptions); - } - } - throw assureDioException(isState ? e.data : e, requestOptions); - }); - } - - Future> _dispatchRequest(RequestOptions reqOpt) async { - final cancelToken = reqOpt.cancelToken; - try { - final stream = await _transformData(reqOpt); - final responseBody = await httpClientAdapter.fetch( - reqOpt, - stream, - cancelToken?.whenCancel, - ); - final headers = Headers.fromMap( - responseBody.headers, - preserveHeaderCase: reqOpt.preserveHeaderCase, - ); - // Make sure headers and [ResponseBody.headers] are the same instance. - responseBody.headers = headers.map; - final ret = Response( - data: null, - headers: headers, - requestOptions: reqOpt, - redirects: responseBody.redirects ?? [], - isRedirect: responseBody.isRedirect, - statusCode: responseBody.statusCode, - statusMessage: responseBody.statusMessage, - extra: responseBody.extra, - ); - final statusOk = reqOpt.validateStatus(responseBody.statusCode); - if (statusOk || reqOpt.receiveDataWhenStatusError == true) { - Object? data = await transformer.transformResponse( - reqOpt, - responseBody, - ); - // Make the response as null before returned as JSON. - if (data is String && - data.isEmpty && - T != dynamic && - T != String && - reqOpt.responseType == ResponseType.json) { - data = null; - } - ret.data = data; - } else { - await responseBody.stream.listen(null).cancel(); - } - checkCancelled(cancelToken); - if (statusOk) { - return ret; - } else { - throw DioException.badResponse( - statusCode: responseBody.statusCode, - requestOptions: reqOpt, - response: ret, - ); - } - } catch (e) { - throw assureDioException(e, reqOpt); - } - } - - bool _isValidToken(String token) { - // from https://www.rfc-editor.org/rfc/rfc2616#page-15 - // - // CTL = - // separators = "(" | ")" | "<" | ">" | "@" - // | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" - // | "{" | "}" | SP | HT - // token = 1* - const String validChars = r' ' - r" ! #$%&' *+ -. 0123456789 " - r' ABCDEFGHIJKLMNOPQRSTUVWXYZ ^_' - r'`abcdefghijklmnopqrstuvwxyz | ~ '; - for (final int codeUnit in token.codeUnits) { - if (codeUnit >= validChars.length || - validChars.codeUnitAt(codeUnit) == 0x20) { - return false; - } - } - return true; - } - - Future?> _transformData(RequestOptions options) async { - if (!_isValidToken(options.method)) { - throw ArgumentError.value(options.method, 'method'); - } - final data = options.data; - if (data != null) { - final Stream> stream; - // Handle the FormData. - int? length; - if (data is Stream) { - if (data is! Stream>) { - throw ArgumentError.value( - data.runtimeType, - 'data', - 'Stream type must be `Stream>`', - ); - } - stream = data; - options.headers.keys.any((String key) { - if (key.toLowerCase() == Headers.contentLengthHeader) { - length = int.parse(options.headers[key].toString()); - return true; - } - return false; - }); - } else if (data is FormData) { - options.headers[Headers.contentTypeHeader] = - '${Headers.multipartFormDataContentType}; ' - 'boundary=${data.boundary}'; - stream = data.finalize(); - length = data.length; - options.headers[Headers.contentLengthHeader] = length.toString(); - } else { - final List bytes; - if (data is Uint8List) { - // Handle binary data which does not need to be transformed. - bytes = data; - } else { - // Call the request transformer. - final transformed = await transformer.transformRequest(options); - if (options.requestEncoder != null) { - final encoded = options.requestEncoder!(transformed, options); - - if (encoded is Future) { - bytes = await encoded; - } else { - bytes = encoded; - } - } else { - // Converts the data to UTF-8 by default. - bytes = utf8.encode(transformed); - } - } - - // Allocate send progress. - length = bytes.length; - options.headers[Headers.contentLengthHeader] = length.toString(); - - final group = >[]; - const size = 1024; - final groupCount = (bytes.length / size).ceil(); - for (int i = 0; i < groupCount; ++i) { - final start = i * size; - group.add(bytes.sublist(start, math.min(start + size, bytes.length))); - } - stream = Stream.fromIterable(group); - } - return addProgress(stream, length, options); - } - return null; - } - - // If the request has been cancelled, stop the request and throw error. - @internal - static void checkCancelled(CancelToken? cancelToken) { - final error = cancelToken?.cancelError; - if (error != null) { - throw error; - } - } - - @internal - static Future listenCancelForAsyncTask( - CancelToken? cancelToken, - Future future, - ) { - return Future.any([ - if (cancelToken != null) cancelToken.whenCancel.then((e) => throw e), - future, - ]); - } - - @internal - static Options checkOptions(String method, Options? options) { - options ??= Options(); - options.method = method; - return options; - } - - @internal - static DioException assureDioException( - Object error, - RequestOptions requestOptions, - ) { - if (error is DioException) { - return error; - } - return DioException( - requestOptions: requestOptions, - error: error, - ); - } - - @internal - static Response assureResponse( - Object response, - RequestOptions requestOptions, - ) { - if (response is! Response) { - return Response( - data: response as T, - requestOptions: requestOptions, - ); - } else if (response is! Response) { - final T data = response.data as T; - final Headers headers; - if (data is ResponseBody) { - headers = Headers.fromMap( - data.headers, - preserveHeaderCase: requestOptions.preserveHeaderCase, - ); - } else { - headers = response.headers; - } - return Response( - data: data, - headers: headers, - requestOptions: response.requestOptions, - statusCode: response.statusCode, - isRedirect: response.isRedirect, - redirects: response.redirects, - statusMessage: response.statusMessage, - extra: response.extra, - ); - } - return response; - } -} diff --git a/dio/lib/src/interceptor.dart b/dio/lib/src/interceptor.dart index bca8981b2..0bbfbd291 100644 --- a/dio/lib/src/interceptor.dart +++ b/dio/lib/src/interceptor.dart @@ -1,4 +1,4 @@ -part of 'dio_mixin.dart'; +part of 'dio.dart'; /// @nodoc enum InterceptorResultType { @@ -297,7 +297,7 @@ class InterceptorsWrapper extends Interceptor with _InterceptorWrapperMixin { /// Interceptors will be executed with FIFO. class Interceptors extends ListMixin { /// Define a nullable list to be capable with growable elements. - final List _list = [const ImplyContentTypeInterceptor()]; + final List _list = [ImplyContentTypeInterceptor()]; @override int get length => _list.length; diff --git a/dio/lib/src/interceptors/imply_content_type.dart b/dio/lib/src/interceptors/imply_content_type.dart index 6664d323d..89081f0ae 100644 --- a/dio/lib/src/interceptors/imply_content_type.dart +++ b/dio/lib/src/interceptors/imply_content_type.dart @@ -1,4 +1,4 @@ -import '../dio_mixin.dart'; +import '../dio.dart'; import '../form_data.dart'; import '../headers.dart'; import '../options.dart'; diff --git a/dio/lib/src/interceptors/log.dart b/dio/lib/src/interceptors/log.dart index 682384334..54599b5a0 100644 --- a/dio/lib/src/interceptors/log.dart +++ b/dio/lib/src/interceptors/log.dart @@ -1,5 +1,5 @@ +import '../dio.dart'; import '../dio_exception.dart'; -import '../dio_mixin.dart'; import '../options.dart'; import '../response.dart'; diff --git a/dio/lib/src/options.dart b/dio/lib/src/options.dart index 50891bccb..cd6a41c1e 100644 --- a/dio/lib/src/options.dart +++ b/dio/lib/src/options.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; -import 'adapter.dart'; +import 'adapters/adapter.dart'; import 'cancel_token.dart'; import 'headers.dart'; import 'transformer.dart'; diff --git a/dio/lib/src/transformers/sync_transformer.dart b/dio/lib/src/transformers/sync_transformer.dart index beb67f202..6817d816b 100644 --- a/dio/lib/src/transformers/sync_transformer.dart +++ b/dio/lib/src/transformers/sync_transformer.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; -import '../adapter.dart'; +import '../adapters/adapter.dart'; import '../headers.dart'; import '../options.dart'; import '../transformer.dart'; diff --git a/dio/test/dio_mixin_test.dart b/dio/test/dio_mixin_test.dart deleted file mode 100644 index 388c17b19..000000000 --- a/dio/test/dio_mixin_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:test/test.dart'; - -void main() { - test('not thrown for implements', () { - expect(_TestDioMixin().interceptors, isA()); - expect(_TestDioMixinExtends().interceptors, isA()); - }); - - test('assureResponse', () { - final requestOptions = RequestOptions(path: ''); - final untypedResponse = Response( - requestOptions: requestOptions, - data: null, - ); - expect(untypedResponse is Response, isFalse); - - final typedResponse = DioMixin.assureResponse( - untypedResponse, - requestOptions, - ); - expect(typedResponse.data, isNull); - }); - - test('throws UnimplementedError when calling download', () { - expectLater( - () => _TestDioMixin().download('a', 'b'), - throwsA(TypeMatcher()), - ); - }); -} - -class _TestDioMixin extends DioMixin implements Dio {} - -class _TestDioMixinExtends extends DioMixin implements Dio {} diff --git a/dio/test/mock/http_mock.mocks.dart b/dio/test/mock/http_mock.mocks.dart index 4193a3f5c..1b2aafd0c 100644 --- a/dio/test/mock/http_mock.mocks.dart +++ b/dio/test/mock/http_mock.mocks.dart @@ -7,7 +7,7 @@ import 'dart:async' as _i4; import 'dart:convert' as _i3; import 'dart:io' as _i2; -import 'package:dio/src/adapter.dart' as _i7; +import 'package:dio/src/adapters/adapter.dart' as _i7; import 'package:dio/src/options.dart' as _i6; import 'package:dio/src/transformer.dart' as _i5; import 'package:mockito/mockito.dart' as _i1; diff --git a/example/lib/extend_dio.dart b/example/lib/extend_dio.dart index 1e2e2020b..7cfe2147a 100644 --- a/example/lib/extend_dio.dart +++ b/example/lib/extend_dio.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart'; import 'package:dio/io.dart'; -class HttpService extends DioForNative { +class HttpService extends Dio { HttpService([BaseOptions? baseOptions]) : super(baseOptions) { options ..baseUrl = 'https://httpbin.org/' From b29f1d81379bfe335121ed10bf4603cd6afa5784 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Thu, 28 Dec 2023 16:27:31 +0100 Subject: [PATCH 2/3] Format --- dio/lib/browser.dart | 3 ++- dio/lib/src/adapters/adapter.dart | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dio/lib/browser.dart b/dio/lib/browser.dart index 7e1489669..5233f8c34 100644 --- a/dio/lib/browser.dart +++ b/dio/lib/browser.dart @@ -1,3 +1,4 @@ export 'src/adapters/browser_adapter.dart' show BrowserHttpClientAdapter; -export 'src/adapters/download/browser_download_adapter.dart' show BrowserDownloadAdapter; +export 'src/adapters/download/browser_download_adapter.dart' + show BrowserDownloadAdapter; export 'src/dio.dart' show Dio; diff --git a/dio/lib/src/adapters/adapter.dart b/dio/lib/src/adapters/adapter.dart index 2e6d4cbb4..c157cb2cd 100644 --- a/dio/lib/src/adapters/adapter.dart +++ b/dio/lib/src/adapters/adapter.dart @@ -4,8 +4,8 @@ import 'dart:typed_data'; import '../options.dart'; import '../redirect_record.dart'; -import 'io_adapter.dart' - if (dart.library.html) 'browser_adapter.dart' as adapter; +import 'io_adapter.dart' if (dart.library.html) 'browser_adapter.dart' + as adapter; /// [HttpAdapter] is a bridge between [Dio] and [HttpClient]. /// From 8d1c2d5c7bb6fac5249d8b598e1038c015fa5ce8 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Thu, 28 Dec 2023 17:05:04 +0100 Subject: [PATCH 3/3] Fix import --- dio/test/adapters_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/dio/test/adapters_test.dart b/dio/test/adapters_test.dart index 9909e9ad3..ed5f355fc 100644 --- a/dio/test/adapters_test.dart +++ b/dio/test/adapters_test.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:test/test.dart';