From acbf115bcf01bfbbaa858952fba7b0babbd81966 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 16 Dec 2022 23:19:49 +0800 Subject: [PATCH] Clean code (#34) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚨 Fix linter rules * ⚡️ Add missing type infer * 🚀 Move around `download` and `downloadUri` * ⚡️ Improve fields' definition with multiple classes * ⚡️ Improve docs and comments * ⚡️ Improve interceptor's internal classes * 🎨 Reorg tests * 🎨 ++ * 📝 Fix broken links * 🎨 `Object` -> `dynamic` in `download()` * 🥅 Improve type checks --- dio/CHANGELOG.md | 4 +- dio/analysis_options.yaml | 9 +- dio/lib/src/adapter.dart | 8 +- dio/lib/src/adapters/browser_adapter.dart | 13 +- dio/lib/src/adapters/io_adapter.dart | 34 ++-- dio/lib/src/cancel_token.dart | 34 ++-- dio/lib/src/dio.dart | 170 ++++++++---------- dio/lib/src/dio/dio_for_browser.dart | 4 +- dio/lib/src/dio/dio_for_native.dart | 151 +++------------- dio/lib/src/dio_mixin.dart | 156 +++++----------- dio/lib/src/form_data.dart | 14 +- dio/lib/src/interceptor.dart | 82 +++++---- .../src/multipart_file/io_multipart_file.dart | 2 + dio/lib/src/options.dart | 18 +- .../browser_progress_stream.dart | 1 + .../progress_stream/io_progress_stream.dart | 1 + dio/lib/src/response.dart | 52 +++--- dio/test/echo_adapter.dart | 35 ---- dio/test/formdata_test.dart | 10 +- dio/test/interceptor_test.dart | 7 +- dio/test/{ => mock}/_formdata | 0 dio/test/{ => mock}/_testfile | 0 .../{mock_adapter.dart => mock/adapters.dart} | 35 +++- dio/test/{ => mock}/test.jpg | Bin dio/test/options_test.dart | 5 +- dio/test/request_test.dart | 12 +- dio/test/upload_stream_test.dart | 4 +- 27 files changed, 348 insertions(+), 513 deletions(-) delete mode 100644 dio/test/echo_adapter.dart rename dio/test/{ => mock}/_formdata (100%) rename dio/test/{ => mock}/_testfile (100%) rename dio/test/{mock_adapter.dart => mock/adapters.dart} (76%) rename dio/test/{ => mock}/test.jpg (100%) diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index 12d2910..1be2b06 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -149,8 +149,8 @@ the subsequent interceptors processing logic more finely (whether to skip them o ### New features - Support Flutter Web. -- Extract [CookieManager](/plugins/cookie_manager) into a separate package(No need for Flutter Web). -- Provides [HTTP/2.0 HttpClientAdapter](/plugins/http2_adapter). +- Extract [CookieManager](../plugins/cookie_manager) into a separate package(No need for Flutter Web). +- Provides [HTTP/2.0 HttpClientAdapter](../plugins/http2_adapter). ### Change List diff --git a/dio/analysis_options.yaml b/dio/analysis_options.yaml index f22f9bc..1f923fa 100644 --- a/dio/analysis_options.yaml +++ b/dio/analysis_options.yaml @@ -4,9 +4,10 @@ analyzer: errors: deprecated_member_use_from_same_package: ignore todo: ignore + linter: rules: - - prefer_final_fields: true - - prefer_final_locals: true - - prefer_final_in_for_each: true - - unnecessary_parenthesis: true + - prefer_final_fields + - prefer_final_locals + - prefer_final_in_for_each + - unnecessary_parenthesis diff --git a/dio/lib/src/adapter.dart b/dio/lib/src/adapter.dart index ac60f4a..da57555 100644 --- a/dio/lib/src/adapter.dart +++ b/dio/lib/src/adapter.dart @@ -63,13 +63,13 @@ class ResponseBody { this.redirects, }); - /// The response stream + /// The response stream. Stream stream; - /// the response headers - late Map> headers; + /// The response headers. + Map> headers; - /// Http status code + /// HTTP status code. int statusCode; /// Returns the reason phrase associated with the status code. diff --git a/dio/lib/src/adapters/browser_adapter.dart b/dio/lib/src/adapters/browser_adapter.dart index 01a8b78..2ba6526 100644 --- a/dio/lib/src/adapters/browser_adapter.dart +++ b/dio/lib/src/adapters/browser_adapter.dart @@ -10,6 +10,7 @@ import '../options.dart'; HttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); +/// The default [HttpClientAdapter] for Web platforms. class BrowserHttpClientAdapter implements HttpClientAdapter { /// These are aborted if the client is closed. final _xhrs = {}; @@ -55,7 +56,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter { final completer = Completer(); xhr.onLoad.first.then((_) { - Uint8List body = (xhr.response as ByteBuffer).asUint8List(); + final Uint8List body = (xhr.response as ByteBuffer).asUint8List(); completer.complete( ResponseBody.fromBytes( body, @@ -117,10 +118,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter { if (duration > sendTimeout) { uploadStopwatch.stop(); completer.completeError( - DioError.sendTimeout( - timeout: sendTimeout, - requestOptions: options, - ), + DioError.sendTimeout(timeout: sendTimeout, requestOptions: options), StackTrace.current, ); xhr.abort(); @@ -186,10 +184,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter { connectTimeoutTimer?.cancel(); try { xhr.abort(); - } catch (e) { - // ignore - } - + } catch (_) {} // xhr.onError will not triggered when xhr.abort() called. // so need to manual throw the cancel error to avoid Future hang ups. // or added xhr.onAbort like axios did https://github.com/axios/axios/blob/master/lib/adapters/xhr.js#L102-L111 diff --git a/dio/lib/src/adapters/io_adapter.dart b/dio/lib/src/adapters/io_adapter.dart index 45c2704..6f62e20 100644 --- a/dio/lib/src/adapters/io_adapter.dart +++ b/dio/lib/src/adapters/io_adapter.dart @@ -1,9 +1,10 @@ import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; + import '../adapter.dart'; -import '../options.dart'; import '../dio_error.dart'; +import '../options.dart'; import '../redirect_record.dart'; @Deprecated('Use IOHttpClientAdapter instead. This will be removed in 6.0.0') @@ -11,11 +12,14 @@ typedef DefaultHttpClientAdapter = IOHttpClientAdapter; typedef OnHttpClientCreate = HttpClient? Function(HttpClient client); typedef ValidateCertificate = bool Function( - X509Certificate? certificate, String host, int port); + X509Certificate? certificate, + String host, + int port, +); HttpClientAdapter createAdapter() => IOHttpClientAdapter(); -/// The default HttpClientAdapter for Dio. +/// The default [HttpClientAdapter] for native platforms. class IOHttpClientAdapter implements HttpClientAdapter { /// [Dio] will create HttpClient when it is needed. /// If [onHttpClientCreate] is provided, [Dio] will call @@ -41,8 +45,9 @@ class IOHttpClientAdapter implements HttpClientAdapter { Future? cancelFuture, ) async { if (_closed) { - throw Exception( - "Can't establish connection after [HttpClientAdapter] closed!"); + throw StateError( + "Can't establish connection after the adapter was closed!", + ); } final httpClient = _configHttpClient(cancelFuture, options.connectTimeout); final reqFuture = httpClient.openUrl(options.method, options.uri); @@ -64,7 +69,7 @@ class IOHttpClientAdapter implements HttpClientAdapter { request = await reqFuture; } - //Set Headers + // Set Headers options.headers.forEach((k, v) { if (v != null) request.headers.set(k, v); }); @@ -102,7 +107,6 @@ class IOHttpClientAdapter implements HttpClientAdapter { }, ); } - await future; } @@ -126,8 +130,11 @@ class IOHttpClientAdapter implements HttpClientAdapter { if (validateCertificate != null) { final host = options.uri.host; final port = options.uri.port; - final isCertApproved = - validateCertificate!(responseStream.certificate, host, port); + final bool isCertApproved = validateCertificate!( + responseStream.certificate, + host, + port, + ); if (!isCertApproved) { throw DioError( requestOptions: options, @@ -138,9 +145,8 @@ class IOHttpClientAdapter implements HttpClientAdapter { } } - final stream = - responseStream.transform(StreamTransformer.fromHandlers( - handleData: (data, sink) { + final stream = responseStream.transform( + StreamTransformer.fromHandlers(handleData: (data, sink) { stopwatch.stop(); final duration = stopwatch.elapsed; final receiveTimeout = options.receiveTimeout; @@ -155,8 +161,8 @@ class IOHttpClientAdapter implements HttpClientAdapter { } else { sink.add(Uint8List.fromList(data)); } - }, - )); + }), + ); final headers = >{}; responseStream.headers.forEach((key, values) { diff --git a/dio/lib/src/cancel_token.dart b/dio/lib/src/cancel_token.dart index e603389..b049deb 100644 --- a/dio/lib/src/cancel_token.dart +++ b/dio/lib/src/cancel_token.dart @@ -1,45 +1,41 @@ import 'dart:async'; + import 'dio_error.dart'; import 'options.dart'; -/// You can cancel a request by using a cancel token. +/// An instance which controls cancellation of [Dio]'s requests, +/// build from [Completer]. +/// +/// You can cancel requests by using a [CancelToken]. /// One token can be shared with different requests. -/// when a token's [cancel] method invoked, all requests -/// with this token will be cancelled. +/// When [cancel] is invoked, all requests using this token will be cancelled. class CancelToken { - CancelToken() { - _completer = Completer(); - } + CancelToken(); - /// Whether is throw by [cancel] - static bool isCancel(DioError e) { - return e.type == DioErrorType.cancel; - } + final Completer _completer = Completer(); - /// If request have been canceled, save the cancel Error. - DioError? _cancelError; + /// Whether the [error] is thrown by [cancel]. + static bool isCancel(DioError error) => error.type == DioErrorType.cancel; - /// If request have been canceled, save the cancel Error. + /// If request have been canceled, save the cancel error. DioError? get cancelError => _cancelError; - - late Completer _completer; + DioError? _cancelError; RequestOptions? requestOptions; - /// whether cancelled + /// Whether the token is cancelled. bool get isCancelled => _cancelError != null; /// When cancelled, this future will be resolved. Future get whenCancel => _completer.future; - /// Cancel the request + /// Cancel the request with the given [reason]. void cancel([Object? reason]) { _cancelError = DioError.requestCancelled( - requestOptions: requestOptions ?? RequestOptions(path: ''), + requestOptions: requestOptions ?? RequestOptions(), reason: reason, stackTrace: StackTrace.current, ); - if (!_completer.isCompleted) { _completer.complete(_cancelError); } diff --git a/dio/lib/src/dio.dart b/dio/lib/src/dio.dart index 1c44657..aab1d90 100644 --- a/dio/lib/src/dio.dart +++ b/dio/lib/src/dio.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import 'adapter.dart'; import 'dio_mixin.dart'; import 'options.dart'; @@ -6,6 +7,7 @@ import 'headers.dart'; import 'cancel_token.dart'; import 'transformer.dart'; import 'response.dart'; + import 'dio/dio_for_native.dart' if (dart.library.html) 'dio/dio_for_browser.dart'; @@ -80,7 +82,7 @@ abstract class Dio { /// Handy method to make http POST request, which is a alias of [dio.fetch(RequestOptions)]. Future> post( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -91,7 +93,7 @@ abstract class Dio { /// Handy method to make http POST request, which is a alias of [dio.fetch(RequestOptions)]. Future> postUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, @@ -101,7 +103,7 @@ abstract class Dio { /// Handy method to make http PUT request, which is a alias of [dio.fetch(RequestOptions)]. Future> put( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -112,7 +114,7 @@ abstract class Dio { /// Handy method to make http PUT request, which is a alias of [dio.fetch(RequestOptions)]. Future> putUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, @@ -122,7 +124,7 @@ abstract class Dio { /// Handy method to make http HEAD request, which is a alias of [dio.fetch(RequestOptions)]. Future> head( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -131,7 +133,7 @@ abstract class Dio { /// Handy method to make http HEAD request, which is a alias of [dio.fetch(RequestOptions)]. Future> headUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, }); @@ -139,7 +141,7 @@ abstract class Dio { /// Handy method to make http DELETE request, which is a alias of [dio.fetch(RequestOptions)]. Future> delete( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -148,7 +150,7 @@ abstract class Dio { /// Handy method to make http DELETE request, which is a alias of [dio.fetch(RequestOptions)]. Future> deleteUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, }); @@ -156,7 +158,7 @@ abstract class Dio { /// Handy method to make http PATCH request, which is a alias of [dio.fetch(RequestOptions)]. Future> patch( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -167,116 +169,102 @@ abstract class Dio { /// Handy method to make http PATCH request, which is a alias of [dio.fetch(RequestOptions)]. Future> patchUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }); - /// Download the file and save it in local. The default http method is "GET", - /// you can custom it by [Options.method]. + /// {@template dio.Dio.download} + /// Download the file and save it in local. The default http method is "GET", + /// you can custom it by [Options.method]. /// - /// [urlPath]: The file url. + /// [urlPath] is the file url. /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback: - /// 1. A path with String type, eg "xs.jpg" - /// 2. A callback `String Function(Headers headers)`; for example: - /// ```dart - /// await dio.download(url,(Headers headers){ - /// // Extra info: redirect counts - /// print(headers.value('redirects')); - /// // Extra info: real uri - /// print(headers.value('uri')); - /// ... - /// return "..."; - /// }); - /// ``` + /// [savePath] is the path to save the downloading file later. it can be a String or + /// a callback: + /// 1. A path with String type, eg "xs.jpg" + /// 2. A callback `String Function(Headers headers)`; for example: + /// ```dart + /// await dio.download( + /// url, + /// (Headers headers) { + /// // Extra info: redirect counts + /// print(headers.value('redirects')); + /// // Extra info: real uri + /// print(headers.value('uri')); + /// // ... + /// return (await getTemporaryDirectory()).path + 'file_name'; + /// }, + /// ); + /// ``` /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. + /// [onReceiveProgress] is the callback to listen downloading progress. + /// Please refer to [ProgressCallback]. /// - /// [deleteOnError] Whether delete the file when error occurs. The default value is [true]. + /// [deleteOnError] whether delete the file when error occurs. + /// The default value is [true]. /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. + /// [lengthHeader] : The real size of original file (not compressed). + /// When file is compressed: + /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 + /// 2. If this value is not 'content-length', maybe a custom header indicates the original + /// file size , the `total` argument of `onProgress` will be this header value. /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: + /// You can also disable the compression by specifying + /// the 'accept-encoding' header value as '*' to assure the value of + /// `total` argument of `onProgress` is not -1. for example: /// - /// await dio.download(url, "./example/flutter.svg", - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + "%"); - /// } - /// }); - + /// ```dart + /// await dio.download( + /// url, + /// (await getTemporaryDirectory()).path + 'flutter.svg', + /// options: Options( + /// headers: {HttpHeaders.acceptEncodingHeader: "*"}, // Disable gzip + /// ), + /// onProgress: (received, total) { + /// if (total != -1) { + /// print((received / total * 100).toStringAsFixed(0) + "%"); + /// } + /// }, + /// ); + /// ``` + /// {@endtemplate} Future download( String urlPath, - savePath, { + dynamic savePath, { ProgressCallback? onReceiveProgress, Map? queryParameters, CancelToken? cancelToken, bool deleteOnError = true, String lengthHeader = Headers.contentLengthHeader, - data, + Object? data, Options? options, }); - /// Download the file and save it in local. The default http method is "GET", - /// you can custom it by [Options.method]. - /// - /// [uri]: The file url. - /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback: - /// 1. A path with String type, eg "xs.jpg" - /// 2. A callback `String Function(Headers)`; for example: - /// ```dart - /// await dio.downloadUri(uri,(Headers headers){ - /// // Extra info: redirect counts - /// print(headers.value('redirects')); - /// // Extra info: real uri - /// print(headers.value('uri')); - /// ... - /// return "..."; - /// }); - /// ``` - /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. - /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. - /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: - /// - /// await dio.downloadUri(uri, "./example/flutter.svg", - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + "%"); - /// } - /// }); + /// {@macro dio.Dio.download} Future downloadUri( Uri uri, - savePath, { + dynamic savePath, { ProgressCallback? onReceiveProgress, CancelToken? cancelToken, bool deleteOnError = true, String lengthHeader = Headers.contentLengthHeader, - data, + Object? data, Options? options, - }); + }) { + return download( + uri.toString(), + savePath, + onReceiveProgress: onReceiveProgress, + lengthHeader: lengthHeader, + deleteOnError: deleteOnError, + cancelToken: cancelToken, + data: data, + options: options, + ); + } /// Make http request with options. /// @@ -285,7 +273,7 @@ abstract class Dio { /// [options] The request options. Future> request( String path, { - data, + Object? data, Map? queryParameters, CancelToken? cancelToken, Options? options, @@ -300,7 +288,7 @@ abstract class Dio { /// [options] The request options. Future> requestUri( Uri uri, { - data, + Object? data, CancelToken? cancelToken, Options? options, ProgressCallback? onSendProgress, diff --git a/dio/lib/src/dio/dio_for_browser.dart b/dio/lib/src/dio/dio_for_browser.dart index c434e8a..83218fd 100644 --- a/dio/lib/src/dio/dio_for_browser.dart +++ b/dio/lib/src/dio/dio_for_browser.dart @@ -1,7 +1,7 @@ +import '../adapters/browser_adapter.dart'; +import '../dio.dart'; import '../dio_mixin.dart'; import '../options.dart'; -import '../dio.dart'; -import '../adapters/browser_adapter.dart'; Dio createDio([BaseOptions? options]) => DioForBrowser(options); diff --git a/dio/lib/src/dio/dio_for_native.dart b/dio/lib/src/dio/dio_for_native.dart index 627b318..af58b17 100644 --- a/dio/lib/src/dio/dio_for_native.dart +++ b/dio/lib/src/dio/dio_for_native.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; + import '../adapter.dart'; import '../cancel_token.dart'; import '../dio_mixin.dart'; @@ -20,57 +21,17 @@ class DioForNative with DioMixin implements Dio { httpClientAdapter = IOHttpClientAdapter(); } - /// Download the file and save it in local. The default http method is "GET", - /// you can custom it by [Options.method]. - /// - /// [urlPath]: The file url. - /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback [String Function(Headers)]: - /// 1. A path with String type, eg "xs.jpg" - /// 2. A callback `String Function(Headers)`; for example: - /// ```dart - /// await dio.download(url,(Headers headers){ - /// // Extra info: redirect counts - /// print(headers.value('redirects')); - /// // Extra info: real uri - /// print(headers.value('uri')); - /// ... - /// return "..."; - /// }); - /// ``` - /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. - /// - /// [deleteOnError] Whether delete the file when error occurs. The default value is [true]. - /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. - /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: - /// - /// await dio.download(url, "./example/flutter.svg", - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + "%"); - /// } - /// }); + /// {@macro dio.Dio.download} @override Future download( String urlPath, - savePath, { + dynamic savePath, { ProgressCallback? onReceiveProgress, Map? queryParameters, CancelToken? cancelToken, bool deleteOnError = true, String lengthHeader = Headers.contentLengthHeader, - data, + Object? data, Options? options, }) async { // We set the `responseType` to [ResponseType.STREAM] to retrieve the @@ -102,33 +63,32 @@ class DioForNative with DioMixin implements Dio { } rethrow; } - - response.headers = Headers.fromMap(response.data!.headers); - - File file; - if (savePath is Function) { - assert(savePath is String Function(Headers), - 'savePath callback type must be `String Function(HttpHeaders)`'); - - // Add real uri and redirect information to headers + final File file; + if (savePath is String Function(Headers)) { + // Add real Uri and redirect information to headers. response.headers ..add('redirects', response.redirects.length.toString()) ..add('uri', response.realUri.toString()); - - file = File(savePath(response.headers) as String); + file = File(savePath(response.headers)); + } else if (savePath is String) { + file = File(savePath); } else { - file = File(savePath.toString()); + throw ArgumentError.value( + savePath.runtimeType, + 'savePath', + 'The type must be `String` or `String Function(Headers)`.', + ); } - //If directory (or file) doesn't exist yet, the entire method fails + // If the directory (or file) doesn't exist yet, the entire method fails. file.createSync(recursive: true); // Shouldn't call file.writeAsBytesSync(list, flush: flush), - // because it can write all bytes by once. Consider that the - // file with a very big size(up 1G), it will be expensive in memory. + // because it can write all bytes by once. Consider that the file is + // a very big size (up to 1 Gigabytes), it will be expensive in memory. RandomAccessFile raf = file.openSync(mode: FileMode.write); - //Create a Completer to notify the success/error state. + // Create a Completer to notify the success/error state. final completer = Completer(); Future future = completer.future; int received = 0; @@ -137,8 +97,9 @@ class DioForNative with DioMixin implements Dio { final stream = response.data!.stream; bool compressed = false; int total = 0; - final contentEncoding = - response.headers.value(Headers.contentEncodingHeader); + final contentEncoding = response.headers.value( + Headers.contentEncodingHeader, + ); if (contentEncoding != null) { compressed = ['gzip', 'deflate', 'compress'].contains(contentEncoding); } @@ -148,10 +109,9 @@ class DioForNative with DioMixin implements Dio { total = int.parse(response.headers.value(lengthHeader) ?? '-1'); } - late StreamSubscription subscription; - Future? asyncWrite; + Future? asyncWrite; bool closed = false; - Future closeAndDelete() async { + Future closeAndDelete() async { if (!closed) { closed = true; await asyncWrite; @@ -162,6 +122,7 @@ class DioForNative with DioMixin implements Dio { } } + late StreamSubscription subscription; subscription = stream.listen( (data) { subscription.pause(); @@ -232,66 +193,4 @@ class DioForNative with DioMixin implements Dio { } return DioMixin.listenCancelForAsyncTask(cancelToken, future); } - - /// Download the file and save it in local. The default http method is "GET", - /// you can custom it by [Options.method]. - /// - /// [uri]: The file url. - /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback: - /// 1. A path with String type, eg "xs.jpg" - /// 2. A callback `String Function(Headers)`; for example: - /// ```dart - /// await dio.downloadUri(uri,(Headers headers){ - /// // Extra info: redirect counts - /// print(headers.value('redirects')); - /// // Extra info: real uri - /// print(headers.value('uri')); - /// ... - /// return "..."; - /// }); - /// ``` - /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. - /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. - /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: - /// - /// await dio.downloadUri(uri, "./example/flutter.svg", - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + "%"); - /// } - /// }); - @override - Future downloadUri( - Uri uri, - savePath, { - ProgressCallback? onReceiveProgress, - CancelToken? cancelToken, - bool deleteOnError = true, - lengthHeader = Headers.contentLengthHeader, - data, - Options? options, - }) { - return download( - uri.toString(), - savePath, - onReceiveProgress: onReceiveProgress, - lengthHeader: lengthHeader, - deleteOnError: deleteOnError, - cancelToken: cancelToken, - data: data, - options: options, - ); - } } diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 937a00d..e303c5b 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -88,7 +88,7 @@ abstract class DioMixin implements Dio { @override Future> post( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -110,7 +110,7 @@ abstract class DioMixin implements Dio { @override Future> postUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, @@ -130,7 +130,7 @@ abstract class DioMixin implements Dio { @override Future> put( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -152,7 +152,7 @@ abstract class DioMixin implements Dio { @override Future> putUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, @@ -172,7 +172,7 @@ abstract class DioMixin implements Dio { @override Future> head( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -190,7 +190,7 @@ abstract class DioMixin implements Dio { @override Future> headUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, }) { @@ -206,7 +206,7 @@ abstract class DioMixin implements Dio { @override Future> delete( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -224,7 +224,7 @@ abstract class DioMixin implements Dio { @override Future> deleteUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, }) { @@ -240,7 +240,7 @@ abstract class DioMixin implements Dio { @override Future> patch( String path, { - data, + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -262,7 +262,7 @@ abstract class DioMixin implements Dio { @override Future> patchUri( Uri uri, { - data, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, @@ -278,103 +278,15 @@ abstract class DioMixin implements Dio { ); } - /// Download the file and save it in local. The default http method is 'GET', - /// you can custom it by [Options.method]. - /// - /// [urlPath]: The file url. - /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback: - /// 1. A path with String type, eg 'xs.jpg' - /// 2. A callback `String Function(HttpHeaders responseHeaders)`; for example: - /// ```dart - /// await dio.download(url,(HttpHeaders responseHeaders){ - /// ... - /// return '...'; - /// }); - /// ``` - /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. - /// - /// [deleteOnError] Whether delete the file when error occurs. The default value is [true]. - /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. - /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: - /// - /// await dio.download(url, './example/flutter.svg', - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + '%'); - /// } - /// }); - - @override - Future download( - String urlPath, - savePath, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError = true, - String lengthHeader = Headers.contentLengthHeader, - data, - Options? options, - }) async { - throw UnsupportedError('Unsupport download API in browser'); - } - - /// Download the file and save it in local. The default http method is 'GET', - /// you can custom it by [Options.method]. - /// - /// [uri]: The file url. - /// - /// [savePath]: The path to save the downloading file later. it can be a String or - /// a callback: - /// 1. A path with String type, eg 'xs.jpg' - /// 2. A callback `String Function(HttpHeaders responseHeaders)`; for example: - /// ```dart - /// await dio.downloadUri(uri,(HttpHeaders responseHeaders){ - /// ... - /// return '...'; - /// }); - /// ``` - /// - /// [onReceiveProgress]: The callback to listen downloading progress. - /// please refer to [ProgressCallback]. - /// - /// [lengthHeader] : The real size of original file (not compressed). - /// When file is compressed: - /// 1. If this value is 'content-length', the `total` argument of `onProgress` will be -1 - /// 2. If this value is not 'content-length', maybe a custom header indicates the original - /// file size , the `total` argument of `onProgress` will be this header value. - /// - /// you can also disable the compression by specifying the 'accept-encoding' header value as '*' - /// to assure the value of `total` argument of `onProgress` is not -1. for example: - /// - /// await dio.downloadUri(uri, './example/flutter.svg', - /// options: Options(headers: {HttpHeaders.acceptEncodingHeader: '*'}), // disable gzip - /// onProgress: (received, total) { - /// if (total != -1) { - /// print((received / total * 100).toStringAsFixed(0) + '%'); - /// } - /// }); @override Future downloadUri( Uri uri, - savePath, { + dynamic savePath, { ProgressCallback? onReceiveProgress, CancelToken? cancelToken, bool deleteOnError = true, String lengthHeader = Headers.contentLengthHeader, - data, + Object? data, Options? options, }) { return download( @@ -389,6 +301,23 @@ abstract class DioMixin implements Dio { ); } + @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( + 'download() is not available in the current environment.', + ); + } + /// Make http request with options. /// /// [uri] The uri. @@ -397,7 +326,7 @@ abstract class DioMixin implements Dio { @override Future> requestUri( Uri uri, { - data, + Object? data, CancelToken? cancelToken, Options? options, ProgressCallback? onSendProgress, @@ -421,7 +350,7 @@ abstract class DioMixin implements Dio { @override Future> request( String path, { - data, + Object? data, Map? queryParameters, CancelToken? cancelToken, Options? options, @@ -519,11 +448,7 @@ abstract class DioMixin implements Dio { return (err, stackTrace) { if (err is! InterceptorState) { err = InterceptorState( - assureDioError( - err, - requestOptions, - stackTrace, - ), + assureDioError(err, requestOptions, stackTrace), ); } @@ -547,8 +472,9 @@ abstract class DioMixin implements Dio { // execute in FIFO order. // Start the request flow - Future future = - Future(() => InterceptorState(requestOptions)); + Future future = Future( + () => InterceptorState(requestOptions), + ); // Add request interceptors to request flow for (final interceptor in interceptors) { @@ -669,11 +595,11 @@ abstract class DioMixin implements Dio { // | "/" | "[" | "]" | "?" | "=" // | "{" | "}" | SP | HT // token = 1* - const validChars = r" " + const String validChars = r" " r" ! #$%&' *+ -. 0123456789 " r" ABCDEFGHIJKLMNOPQRSTUVWXYZ ^_" r"`abcdefghijklmnopqrstuvwxyz | ~ "; - for (int codeUnit in token.codeUnits) { + for (final int codeUnit in token.codeUnits) { if (codeUnit >= validChars.length || validChars.codeUnitAt(codeUnit) == 0x20) { return false; @@ -751,7 +677,9 @@ abstract class DioMixin implements Dio { } static Future listenCancelForAsyncTask( - CancelToken? cancelToken, Future future) { + CancelToken? cancelToken, + Future future, + ) { return Future.any([ if (cancelToken != null) cancelToken.whenCancel.then((e) => throw e), future, @@ -765,7 +693,7 @@ abstract class DioMixin implements Dio { } static DioError assureDioError( - err, + Object err, RequestOptions requestOptions, StackTrace? sourceStackTrace, ) { @@ -781,7 +709,7 @@ abstract class DioMixin implements Dio { } static Response assureResponse( - response, [ + Object response, [ RequestOptions? requestOptions, ]) { if (response is! Response) { diff --git a/dio/lib/src/form_data.dart b/dio/lib/src/form_data.dart index cf7d42b..7df9ed2 100644 --- a/dio/lib/src/form_data.dart +++ b/dio/lib/src/form_data.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:math'; +import 'dart:math' as math; import 'multipart_file.dart'; import 'options.dart'; @@ -63,7 +63,7 @@ class FormData { void _init() { // Assure the boundary unpredictable and unique - final random = Random(); + final random = math.Random(); _boundary = _boundaryPrefix + random.nextInt(4294967296).toString().padLeft(10, '0'); } @@ -146,7 +146,7 @@ class FormData { Stream> finalize() { if (isFinalized) { - throw StateError("Can't finalize a finalized MultipartFile."); + throw StateError('Already finalized.'); } _isFinalized = true; final controller = StreamController>(sync: false); @@ -167,8 +167,10 @@ class FormData { Future.forEach>(files, (file) { writeAscii('--$boundary\r\n'); writeAscii(_headerForFile(file)); - return writeStreamToSink(file.value.finalize(), controller) - .then((_) => writeLine()); + return writeStreamToSink( + file.value.finalize(), + controller, + ).then((_) => writeLine()); }).then((_) { writeAscii('--$boundary--\r\n'); controller.close(); @@ -176,7 +178,7 @@ class FormData { return controller.stream; } - ///Transform the entire FormData contents as a list of bytes asynchronously. + /// Transform the entire FormData contents as a list of bytes asynchronously. Future> readAsBytes() { return Future(() => finalize().reduce((a, b) => [...a, ...b])); } diff --git a/dio/lib/src/interceptor.dart b/dio/lib/src/interceptor.dart index 8ee90e0..3b35384 100644 --- a/dio/lib/src/interceptor.dart +++ b/dio/lib/src/interceptor.dart @@ -13,13 +13,13 @@ enum InterceptorResultType { /// Internal class, It is used to pass state between current and next interceptors. /// @nodoc class InterceptorState { - InterceptorState(this.data, [this.type = InterceptorResultType.next]); + const InterceptorState(this.data, [this.type = InterceptorResultType.next]); - T data; - InterceptorResultType type; + final T data; + final InterceptorResultType type; } -class _BaseHandler { +abstract class _BaseHandler { final _completer = Completer(); void Function()? _processNextInQueue; @@ -42,8 +42,10 @@ class RequestInterceptorHandler extends _BaseHandler { /// /// [response]: Response object to return. /// [callFollowingResponseInterceptor]: Whether to call the response interceptor(s). - void resolve(Response response, - [bool callFollowingResponseInterceptor = false]) { + void resolve( + Response response, [ + bool callFollowingResponseInterceptor = false, + ]) { _completer.complete( InterceptorState( response, @@ -85,8 +87,7 @@ class ResponseInterceptorHandler extends _BaseHandler { _processNextInQueue?.call(); } - /// Return the response directly! Other response interceptor(s) will not be executed. - /// [response]: Response object to return. + /// Return the response directly! Other response i02 void resolve(Response response) { _completer.complete( InterceptorState( @@ -153,12 +154,12 @@ class ErrorInterceptorHandler extends _BaseHandler { } } -/// Dio instance may have interceptor(s) by which you can intercept -/// requests/responses/errors before they are handled by `then` or `catchError`. -/// See also: -/// - [InterceptorsWrapper] A helper class to create Interceptor(s). -/// - [QueuedInterceptor] Serialize the request/response/error before they enter the interceptor. -/// - [QueuedInterceptorsWrapper] A helper class to create QueuedInterceptor(s). +/// Dio instance may have interceptor(s) by which you can intercept +/// requests/responses/errors before they are handled by `then` or `catchError`. +/// See also: +/// - [InterceptorsWrapper] A helper class to create Interceptor(s). +/// - [QueuedInterceptor] Serialize the request/response/error before they enter the interceptor. +/// - [QueuedInterceptorsWrapper] A helper class to create QueuedInterceptor(s). class Interceptor { const Interceptor(); @@ -174,8 +175,9 @@ class Interceptor { void onRequest( RequestOptions options, RequestInterceptorHandler handler, - ) => - handler.next(options); + ) { + handler.next(options); + } /// The callback will be executed on success. /// If you want to continue the response, call [handler.next]. @@ -189,8 +191,9 @@ class Interceptor { void onResponse( Response response, ResponseInterceptorHandler handler, - ) => - handler.next(response); + ) { + handler.next(response); + } /// The callback will be executed on error. /// @@ -206,8 +209,9 @@ class Interceptor { void onError( DioError err, ErrorInterceptorHandler handler, - ) => - handler.next(err); + ) { + handler.next(err); + } } typedef InterceptorSendCallback = void Function( @@ -216,22 +220,25 @@ typedef InterceptorSendCallback = void Function( ); typedef InterceptorSuccessCallback = void Function( - Response e, + Response e, ResponseInterceptorHandler handler, ); typedef InterceptorErrorCallback = void Function( - DioError e, ErrorInterceptorHandler handler); + DioError e, + ErrorInterceptorHandler handler, +); mixin _InterceptorWrapperMixin on Interceptor { InterceptorSendCallback? _onRequest; - InterceptorSuccessCallback? _onResponse; - InterceptorErrorCallback? _onError; @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + void onRequest( + RequestOptions options, + RequestInterceptorHandler handler, + ) { if (_onRequest != null) { _onRequest!(options, handler); } else { @@ -240,7 +247,10 @@ mixin _InterceptorWrapperMixin on Interceptor { } @override - void onResponse(Response response, ResponseInterceptorHandler handler) { + void onResponse( + Response response, + ResponseInterceptorHandler handler, + ) { if (_onResponse != null) { _onResponse!(response, handler); } else { @@ -249,7 +259,10 @@ mixin _InterceptorWrapperMixin on Interceptor { } @override - void onError(DioError err, ErrorInterceptorHandler handler) { + void onError( + DioError err, + ErrorInterceptorHandler handler, + ) { if (_onError != null) { _onError!(err, handler); } else { @@ -298,7 +311,7 @@ class Interceptors extends ListMixin { Interceptor operator [](int index) => _list[index]; @override - void operator []=(int index, value) { + void operator []=(int index, Interceptor value) { if (_list.length == index) { _list.add(value); } else { @@ -337,11 +350,17 @@ class QueuedInterceptor extends Interceptor { _handleQueue(_requestQueue, options, handler, onRequest); } - void _handleResponse(Response response, ResponseInterceptorHandler handler) { + void _handleResponse( + Response response, + ResponseInterceptorHandler handler, + ) { _handleQueue(_responseQueue, response, handler, onResponse); } - void _handleError(DioError err, ErrorInterceptorHandler handler) { + void _handleError( + DioError err, + ErrorInterceptorHandler handler, + ) { _handleQueue(_errorQueue, err, handler, onError); } @@ -382,7 +401,8 @@ void Function() _processNextTaskInQueueCallback(_TaskQueue taskQueue, cb) { } /// [QueuedInterceptorsWrapper] is a helper class, which is used to conveniently -/// create QueuedInterceptor(s). +/// create [QueuedInterceptor]s. +/// /// See also: /// - [Interceptor] /// - [InterceptorsWrapper] diff --git a/dio/lib/src/multipart_file/io_multipart_file.dart b/dio/lib/src/multipart_file/io_multipart_file.dart index 783f71f..b4d79d7 100644 --- a/dio/lib/src/multipart_file/io_multipart_file.dart +++ b/dio/lib/src/multipart_file/io_multipart_file.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:io'; + import 'package:http_parser/http_parser.dart'; import 'package:path/path.dart' as p; + import '../multipart_file.dart'; Future multipartFileFromPath( diff --git a/dio/lib/src/options.dart b/dio/lib/src/options.dart index e011a2c..caeea49 100644 --- a/dio/lib/src/options.dart +++ b/dio/lib/src/options.dart @@ -170,7 +170,7 @@ class BaseOptions extends _RequestConfig with OptionsMixin { } mixin OptionsMixin { - /// Request base url, it can contain sub path, like: "https://www.google.com/api/". + /// Request base url, it can contain sub paths like: https://pub.dev/api/. late String baseUrl; /// Common query parameters. @@ -279,7 +279,7 @@ class Options { RequestOptions compose( BaseOptions baseOpt, String path, { - data, + Object? data, Map? queryParameters, CancelToken? cancelToken, Options? options, @@ -438,16 +438,16 @@ class Options { class RequestOptions extends _RequestConfig with OptionsMixin { RequestOptions({ + this.path = '', + this.data, + this.onReceiveProgress, + this.onSendProgress, + this.cancelToken, String? method, Duration? sendTimeout, Duration? receiveTimeout, Duration? connectTimeout, - this.data, - required this.path, Map? queryParameters, - this.onReceiveProgress, - this.onSendProgress, - this.cancelToken, String? baseUrl, Map? extra, Map? headers, @@ -585,8 +585,8 @@ class RequestOptions extends _RequestConfig with OptionsMixin { /// object wrapping the actual List value and the desired format. dynamic data; - /// If the `path` starts with 'http(s)', the `baseURL` will be ignored, otherwise, - /// it will be combined and then resolved with the baseUrl. + /// If the `path` starts with 'http(s)', the `baseURL` will be ignored, + /// otherwise, it will be combined and then resolved with the baseUrl. String path; CancelToken? cancelToken; diff --git a/dio/lib/src/progress_stream/browser_progress_stream.dart b/dio/lib/src/progress_stream/browser_progress_stream.dart index 605e0f6..43a4e6f 100644 --- a/dio/lib/src/progress_stream/browser_progress_stream.dart +++ b/dio/lib/src/progress_stream/browser_progress_stream.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:typed_data'; + import 'package:dio/dio.dart'; Stream addProgress( diff --git a/dio/lib/src/progress_stream/io_progress_stream.dart b/dio/lib/src/progress_stream/io_progress_stream.dart index 2185ac0..53c1a3a 100644 --- a/dio/lib/src/progress_stream/io_progress_stream.dart +++ b/dio/lib/src/progress_stream/io_progress_stream.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:typed_data'; + import 'package:dio/dio.dart'; Stream addProgress( diff --git a/dio/lib/src/response.dart b/dio/lib/src/response.dart index a32a2a7..9476873 100644 --- a/dio/lib/src/response.dart +++ b/dio/lib/src/response.dart @@ -7,29 +7,22 @@ import 'redirect_record.dart'; class Response { Response({ this.data, - Headers? headers, required this.requestOptions, - this.isRedirect, this.statusCode, this.statusMessage, - List? redirects, - Map? extra, - }) { - this.headers = headers ?? Headers(); - this.extra = extra ?? {}; - this.redirects = redirects ?? []; - } + this.isRedirect = false, + this.redirects = const [], + this.extra = const {}, + Headers? headers, + }) : headers = headers ?? Headers(); /// Response body. may have been transformed, please refer to [ResponseType]. T? data; - /// Response headers. - late Headers headers; - /// The corresponding request info. - late RequestOptions requestOptions; + RequestOptions requestOptions; - /// Http status code. + /// HTTP status code. int? statusCode; /// Returns the reason phrase associated with the status code. @@ -37,28 +30,31 @@ class Response { /// to. Setting the reason phrase after writing to the body. String? statusMessage; - /// Custom field that you can retrieve it later in `then`. - late Map extra; - - /// Returns the series of redirects this connection has been through. The - /// list will be empty if no redirects were followed. [redirects] will be - /// updated both in the case of an automatic and a manual redirect. - /// - /// ** Attention **: Whether this field is available depends on whether the - /// implementation of the adapter supports it or not. - late List redirects; - /// Whether this response is a redirect. /// ** Attention **: Whether this field is available depends on whether the /// implementation of the adapter supports it or not. - bool? isRedirect; + bool isRedirect; - /// Return the final real request uri (maybe redirect). + /// The series of redirects this connection has been through. The list will be + /// empty if no redirects were followed. [redirects] will be updated both + /// in the case of an automatic and a manual redirect. /// /// ** Attention **: Whether this field is available depends on whether the /// implementation of the adapter supports it or not. + List redirects; + + /// Custom fields that are constructed in the [RequestOptions]. + Map extra; + + /// Response headers. + Headers headers; + + /// Return the final real request URI (may be redirected). + /// + /// Note: Whether the field is available depends on whether the adapter + /// supports or not. Uri get realUri => - (redirects.isNotEmpty) ? redirects.last.location : requestOptions.uri; + redirects.isNotEmpty ? redirects.last.location : requestOptions.uri; /// We are more concerned about `data` field. @override diff --git a/dio/test/echo_adapter.dart b/dio/test/echo_adapter.dart deleted file mode 100644 index 72acb5d..0000000 --- a/dio/test/echo_adapter.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:dio/io.dart'; -import 'package:dio/dio.dart'; - -class EchoAdapter implements HttpClientAdapter { - static const mockHost = 'mockserver'; - static const mockBase = 'http://$mockHost'; - final _adapter = IOHttpClientAdapter(); - - @override - Future fetch( - RequestOptions options, - Stream? requestStream, - Future? cancelFuture, - ) async { - final uri = options.uri; - - if (uri.host == mockHost) { - if (requestStream != null) { - return ResponseBody(requestStream, 200); - } else { - return ResponseBody.fromString(uri.path, 200); - } - } - - return _adapter.fetch(options, requestStream, cancelFuture); - } - - @override - void close({bool force = false}) { - _adapter.close(force: force); - } -} diff --git a/dio/test/formdata_test.dart b/dio/test/formdata_test.dart index b659146..9a3312f 100644 --- a/dio/test/formdata_test.dart +++ b/dio/test/formdata_test.dart @@ -17,14 +17,14 @@ void main() async { }), 'files': [ await MultipartFile.fromFile( - '../dio/test/_testfile', + 'test/mock/_testfile', filename: '1.txt', headers: { 'test': ['b'] }, ), MultipartFile.fromFileSync( - '../dio/test/_testfile', + 'test/mock/_testfile', filename: '2.txt', headers: { 'test': ['c'] @@ -33,7 +33,7 @@ void main() async { ] }); final fmStr = await fm.readAsBytes(); - final f = File('../dio/test/_formdata'); + final f = File('test/mock/_formdata'); String content = f.readAsStringSync(); content = content.replaceAll('--dio-boundary-3788753558', fm.boundary); String actual = utf8.decode(fmStr, allowMalformed: true); @@ -55,7 +55,7 @@ void main() async { )); fm1.files.add(MapEntry( 'files', - await MultipartFile.fromFile('../dio/test/_testfile', + await MultipartFile.fromFile('test/mock/_testfile', filename: '1.txt', headers: { 'test': ['b'] @@ -63,7 +63,7 @@ void main() async { )); fm1.files.add(MapEntry( 'files', - await MultipartFile.fromFile('../dio/test/_testfile', + await MultipartFile.fromFile('test/mock/_testfile', filename: '2.txt', headers: { 'test': ['c'] diff --git a/dio/test/interceptor_test.dart b/dio/test/interceptor_test.dart index 9becc89..7a51c91 100644 --- a/dio/test/interceptor_test.dart +++ b/dio/test/interceptor_test.dart @@ -1,8 +1,9 @@ import 'dart:async'; + import 'package:dio/dio.dart'; import 'package:test/test.dart'; -import 'mock_adapter.dart'; -import 'echo_adapter.dart'; + +import 'mock/adapters.dart'; class MyInterceptor extends Interceptor { int requestCount = 0; @@ -363,7 +364,7 @@ void main() { response = await dio.get('${urlNotFound}2'); expect(response.data, 'fake data'); expect( - dio.get('${urlNotFound}3').catchError((e) => throw (e as DioError)), + dio.get('${urlNotFound}3').catchError((e) => throw e as DioError), throwsA(isA()), ); }); diff --git a/dio/test/_formdata b/dio/test/mock/_formdata similarity index 100% rename from dio/test/_formdata rename to dio/test/mock/_formdata diff --git a/dio/test/_testfile b/dio/test/mock/_testfile similarity index 100% rename from dio/test/_testfile rename to dio/test/mock/_testfile diff --git a/dio/test/mock_adapter.dart b/dio/test/mock/adapters.dart similarity index 76% rename from dio/test/mock_adapter.dart rename to dio/test/mock/adapters.dart index 1137d8e..b2043df 100644 --- a/dio/test/mock_adapter.dart +++ b/dio/test/mock/adapters.dart @@ -3,12 +3,12 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:dio/io.dart'; import 'package:dio/dio.dart'; +import 'package:dio/io.dart'; class MockAdapter implements HttpClientAdapter { static const mockHost = 'mockserver'; - static const mockBase = 'http://$mockHost'; + static const mockBase = 'https://$mockHost'; final _adapter = IOHttpClientAdapter(); @override @@ -99,3 +99,34 @@ class MockAdapter implements HttpClientAdapter { _adapter.close(force: force); } } + +/// [EchoAdapter] will return the data as-is +/// if the host is [EchoAdapter.mockHost]. +class EchoAdapter implements HttpClientAdapter { + static const String mockHost = 'mockserver'; + static const String mockBase = 'https://$mockHost'; + + final HttpClientAdapter _adapter = IOHttpClientAdapter(); + + @override + Future fetch( + RequestOptions options, + Stream? requestStream, + Future? cancelFuture, + ) async { + final Uri uri = options.uri; + if (uri.host == mockHost) { + if (requestStream != null) { + return ResponseBody(requestStream, 200); + } else { + return ResponseBody.fromString(uri.path, 200); + } + } + return _adapter.fetch(options, requestStream, cancelFuture); + } + + @override + void close({bool force = false}) { + _adapter.close(force: force); + } +} diff --git a/dio/test/test.jpg b/dio/test/mock/test.jpg similarity index 100% rename from dio/test/test.jpg rename to dio/test/mock/test.jpg diff --git a/dio/test/options_test.dart b/dio/test/options_test.dart index 310fae9..74484bd 100644 --- a/dio/test/options_test.dart +++ b/dio/test/options_test.dart @@ -7,8 +7,7 @@ import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:test/test.dart'; -import 'echo_adapter.dart'; -import 'mock_adapter.dart'; +import 'mock/adapters.dart'; void main() { test('#test options', () { @@ -331,7 +330,7 @@ void main() { const String separators = "\t\n\r()<>@,;:\\/[]?={}"; for (int i = 0; i < separators.length; i++) { - String separator = separators.substring(i, i + 1); + final String separator = separators.substring(i, i + 1); testInvalidArgumentException(separator); testInvalidArgumentException("${separator}CONNECT"); testInvalidArgumentException("CONN${separator}ECT"); diff --git a/dio/test/request_test.dart b/dio/test/request_test.dart index e5c8565..84d3098 100644 --- a/dio/test/request_test.dart +++ b/dio/test/request_test.dart @@ -4,8 +4,10 @@ @TestOn('vm') import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:test/test.dart'; + import 'utils.dart'; void main() { @@ -33,7 +35,6 @@ void main() { }); test('#test restful APIs', () async { Response response; - // test get response = await dio.get( '/test', @@ -45,9 +46,12 @@ void main() { expect(response.headers.value('single'), equals('value')); const map = {'content': 'I am playload'}; - // test post - response = await dio.post('/test', data: map); + response = await dio.post( + '/test', + data: map, + options: Options(contentType: Headers.jsonContentType), + ); expect(response.data['method'], 'POST'); expect(response.data['body'], jsonEncode(map)); @@ -88,7 +92,7 @@ void main() { }); test('#test multi value headers', () async { - Response response = await dio.get( + final Response response = await dio.get( '/multi-value-header', options: Options( headers: { diff --git a/dio/test/upload_stream_test.dart b/dio/test/upload_stream_test.dart index 4cd502c..09d053c 100644 --- a/dio/test/upload_stream_test.dart +++ b/dio/test/upload_stream_test.dart @@ -26,7 +26,7 @@ void main() { }); test('file stream', () async { - final f = File('../dio/test/test.jpg'); + final f = File('test/mock/test.jpg'); final r = await dio.put( '/put', data: f.openRead(), @@ -42,7 +42,7 @@ void main() { }, testOn: "vm"); test('file stream', () async { - final f = File('../dio/test/test.jpg'); + final f = File('test/mock/test.jpg'); final r = await dio.put( '/put', data: f.readAsBytes().asStream(),