From 8e9759736cf443f6c9335ee84254f91118ecba0b Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 14 Mar 2024 11:17:20 +0800 Subject: [PATCH 1/9] fix: Uncatch error in interceptor Signed-off-by: CaiJingLong --- dio/lib/src/dio_mixin.dart | 4 ++-- dio/lib/src/interceptor.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 9ec104f1a..15528cb2b 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -382,9 +382,9 @@ abstract class DioMixin implements Dio { if (state.type == InterceptorResultType.next) { return listenCancelForAsyncTask( requestOptions.cancelToken, - Future(() { + Future(() async { final requestHandler = RequestInterceptorHandler(); - interceptor(state.data as RequestOptions, requestHandler); + await interceptor(state.data as RequestOptions, requestHandler); return requestHandler.future; }), ); diff --git a/dio/lib/src/interceptor.dart b/dio/lib/src/interceptor.dart index bca8981b2..51f7c289a 100644 --- a/dio/lib/src/interceptor.dart +++ b/dio/lib/src/interceptor.dart @@ -207,7 +207,7 @@ class Interceptor { } /// The signature of [Interceptor.onRequest]. -typedef InterceptorSendCallback = void Function( +typedef InterceptorSendCallback = FutureOr Function( RequestOptions options, RequestInterceptorHandler handler, ); From 5b5e5eba697e44e71631173466cbb1a1d5e769a8 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 14 Mar 2024 13:18:06 +0800 Subject: [PATCH 2/9] test: Add test case Signed-off-by: CaiJingLong --- dio/test/interceptor_test.dart | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dio/test/interceptor_test.dart b/dio/test/interceptor_test.dart index b757e4f71..6e94278c2 100644 --- a/dio/test/interceptor_test.dart +++ b/dio/test/interceptor_test.dart @@ -318,6 +318,29 @@ void main() { expect(response.data['errCode'], 0); }); + test('throw error on request', () async { + final dio = Dio(); + + const errorMsg = 'interceptor error'; + + dio.interceptors.add(InterceptorsWrapper( + onResponse: (response, handler) { + throw UnsupportedError('interceptor error'); + }, + )); + + expect( + dio.get('https://www.cloudflare.com'), + throwsA( + isA().having( + (dioException) => dioException.error, + 'Exception', + isA() + .having((p0) => p0.message, 'message', errorMsg)), + ), + ); + }); + group(ImplyContentTypeInterceptor, () { Dio createDio() { final dio = Dio(); From c6d8c49cf2c6d4907cbaf8a902e211d937c77758 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 14 Mar 2024 13:30:52 +0800 Subject: [PATCH 3/9] fix: for test pass Signed-off-by: CaiJingLong --- dio/lib/src/dio_mixin.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 15528cb2b..38ec18bda 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -384,7 +384,11 @@ abstract class DioMixin implements Dio { requestOptions.cancelToken, Future(() async { final requestHandler = RequestInterceptorHandler(); - await interceptor(state.data as RequestOptions, requestHandler); + final res = + interceptor(state.data as RequestOptions, requestHandler); + if (res is Future) { + await res; + } return requestHandler.future; }), ); From eb152a240a71234a3f9147e9da2c2730c2a35b0a Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 14 Mar 2024 13:38:12 +0800 Subject: [PATCH 4/9] chore: inline code for test Signed-off-by: CaiJingLong --- dio/test/interceptor_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dio/test/interceptor_test.dart b/dio/test/interceptor_test.dart index 6e94278c2..3b5a6b704 100644 --- a/dio/test/interceptor_test.dart +++ b/dio/test/interceptor_test.dart @@ -325,7 +325,7 @@ void main() { dio.interceptors.add(InterceptorsWrapper( onResponse: (response, handler) { - throw UnsupportedError('interceptor error'); + throw UnsupportedError(errorMsg); }, )); From 84aa7730fe567d00453cb9d2b2902d45ba9d58cf Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 14 Mar 2024 14:03:16 +0800 Subject: [PATCH 5/9] test: fixed problem in unit tests Signed-off-by: CaiJingLong --- dio/test/interceptor_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dio/test/interceptor_test.dart b/dio/test/interceptor_test.dart index 3b5a6b704..92c2d71ad 100644 --- a/dio/test/interceptor_test.dart +++ b/dio/test/interceptor_test.dart @@ -324,7 +324,7 @@ void main() { const errorMsg = 'interceptor error'; dio.interceptors.add(InterceptorsWrapper( - onResponse: (response, handler) { + onRequest: (response, handler) { throw UnsupportedError(errorMsg); }, )); From b9ae0159013f5b2122a1fbb10064fa9360a9d4ab Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 15 Mar 2024 10:41:08 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20More=20callbacks=20and?= =?UTF-8?q?=20unify=20usages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dio/lib/src/dio_mixin.dart | 46 ++++++++++++++++++++---------------- dio/lib/src/interceptor.dart | 4 ++-- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 38ec18bda..3c7db7fb6 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -375,7 +375,7 @@ abstract class DioMixin implements Dio { // 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, + InterceptorSendCallback cb, ) { return (dynamic incomingState) async { final state = incomingState as InterceptorState; @@ -383,25 +383,23 @@ abstract class DioMixin implements Dio { return listenCancelForAsyncTask( requestOptions.cancelToken, Future(() async { - final requestHandler = RequestInterceptorHandler(); - final res = - interceptor(state.data as RequestOptions, requestHandler); - if (res is Future) { - await res; + final handler = RequestInterceptorHandler(); + final callback = cb(state.data as RequestOptions, handler); + if (callback is Future) { + await callback; } - return requestHandler.future; + return handler.future; }), ); - } else { - return state; } + 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, + InterceptorSuccessCallback cb, ) { return (dynamic incomingState) async { final state = incomingState as InterceptorState; @@ -409,10 +407,13 @@ abstract class DioMixin implements Dio { state.type == InterceptorResultType.resolveCallFollowing) { return listenCancelForAsyncTask( requestOptions.cancelToken, - Future(() { - final responseHandler = ResponseInterceptorHandler(); - interceptor(state.data as Response, responseHandler); - return responseHandler.future; + Future(() async { + final handler = ResponseInterceptorHandler(); + final callback = cb(state.data as Response, handler); + if (callback is Future) { + await callback; + } + return handler.future; }), ); } else { @@ -424,16 +425,19 @@ abstract class DioMixin implements Dio { // 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, + InterceptorErrorCallback cb, ) { 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; + final handler = ErrorInterceptorHandler(); + final callback = cb(state.data, handler); + if (callback is Future) { + await callback; + } + return handler.future; } // The request has already been cancelled, @@ -441,15 +445,15 @@ abstract class DioMixin implements Dio { if (state.data is DioException && state.data.type == DioExceptionType.cancel) { return handleError(); - } else if (state.type == InterceptorResultType.next || + } + if (state.type == InterceptorResultType.next || state.type == InterceptorResultType.rejectCallFollowing) { return listenCancelForAsyncTask( requestOptions.cancelToken, Future(handleError), ); - } else { - throw error; } + throw error; }; } diff --git a/dio/lib/src/interceptor.dart b/dio/lib/src/interceptor.dart index 51f7c289a..5259e8ba5 100644 --- a/dio/lib/src/interceptor.dart +++ b/dio/lib/src/interceptor.dart @@ -213,13 +213,13 @@ typedef InterceptorSendCallback = FutureOr Function( ); /// The signature of [Interceptor.onResponse]. -typedef InterceptorSuccessCallback = void Function( +typedef InterceptorSuccessCallback = FutureOr Function( Response response, ResponseInterceptorHandler handler, ); /// The signature of [Interceptor.onError]. -typedef InterceptorErrorCallback = void Function( +typedef InterceptorErrorCallback = FutureOr Function( DioException error, ErrorInterceptorHandler handler, ); From 834f5eea8c339f21195aaffd1d4f1a221621e1b8 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 15 Mar 2024 10:41:33 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E2=9C=85=20++?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dio/test/interceptor_test.dart | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/dio/test/interceptor_test.dart b/dio/test/interceptor_test.dart index 92c2d71ad..097515ecd 100644 --- a/dio/test/interceptor_test.dart +++ b/dio/test/interceptor_test.dart @@ -318,25 +318,27 @@ void main() { expect(response.data['errCode'], 0); }); - test('throw error on request', () async { + test('Caught exceptions before handler called', () async { final dio = Dio(); - const errorMsg = 'interceptor error'; - - dio.interceptors.add(InterceptorsWrapper( - onRequest: (response, handler) { - throw UnsupportedError(errorMsg); - }, - )); - + dio.interceptors.add( + InterceptorsWrapper( + // TODO(EVERYONE): Remove the ignorance once we migrated to a higher version of Dart. + // ignore: void_checks + onRequest: (response, handler) { + throw UnsupportedError(errorMsg); + }, + ), + ); expect( dio.get('https://www.cloudflare.com'), throwsA( isA().having( - (dioException) => dioException.error, - 'Exception', - isA() - .having((p0) => p0.message, 'message', errorMsg)), + (dioException) => dioException.error, + 'Exception', + isA() + .having((e) => e.message, 'message', errorMsg), + ), ), ); }); From e1399ce46ae55cdfea0e8826b4f09fd94e081c86 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 15 Mar 2024 10:42:40 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=94=A5=20Remove=20another=20`.catchEr?= =?UTF-8?q?ror`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dio/lib/src/dio_mixin.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 3c7db7fb6..9c5507b36 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -503,12 +503,13 @@ abstract class DioMixin implements Dio { future = future.catchError(errorInterceptorWrapper(fun)); } // Normalize errors, converts errors to [DioException]. - return future.then>((data) { + try { + final data = await future; return assureResponse( data is InterceptorState ? data.data : data, requestOptions, ); - }).catchError((Object e) { + } catch (e) { final isState = e is InterceptorState; if (isState) { if (e.type == InterceptorResultType.resolve) { @@ -516,7 +517,7 @@ abstract class DioMixin implements Dio { } } throw assureDioException(isState ? e.data : e, requestOptions); - }); + } } Future> _dispatchRequest(RequestOptions reqOpt) async { From 14f4b239a4cdc5ae0ecf2d8276d5afed43d5fd8d Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 15 Mar 2024 11:58:10 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=93=9D=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dio/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index f1ab769e6..7c0b42bb6 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -6,6 +6,7 @@ See the [Migration Guide][] for the complete breaking changes list.** ## Unreleased - Fix `receiveTimeout` throws exception after the request has been cancelled. +- Catch sync/async exceptions in interceptors' handlers. ## 5.4.1