From bbb3394520c7f44474394cc70c93c9ee8e9acdac Mon Sep 17 00:00:00 2001 From: sunhapper Date: Sat, 19 Aug 2023 18:46:36 +0800 Subject: [PATCH] feat(http2):add sendTimeout and receiveTimeout in Http2Adapter --- plugins/http2_adapter/CHANGELOG.md | 2 +- .../http2_adapter/lib/src/http2_adapter.dart | 30 ++++++++++++-- plugins/http2_adapter/test/http2_test.dart | 39 ++++++++++++++++++ plugins/http2_adapter/test/mock/flutter.png | Bin 0 -> 1529 bytes 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 plugins/http2_adapter/test/mock/flutter.png diff --git a/plugins/http2_adapter/CHANGELOG.md b/plugins/http2_adapter/CHANGELOG.md index 039c8a1f0..47ca42f67 100644 --- a/plugins/http2_adapter/CHANGELOG.md +++ b/plugins/http2_adapter/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -*None.* +- Add send timeout and receive timeout ## 2.3.1+1 diff --git a/plugins/http2_adapter/lib/src/http2_adapter.dart b/plugins/http2_adapter/lib/src/http2_adapter.dart index 57b59c9b7..cb8add355 100644 --- a/plugins/http2_adapter/lib/src/http2_adapter.dart +++ b/plugins/http2_adapter/lib/src/http2_adapter.dart @@ -89,11 +89,21 @@ class Http2Adapter implements HttpClientAdapter { } if (hasRequestData) { - await requestStream!.listen((data) { + final requestStreamFuture = requestStream!.listen((data) { stream.outgoingMessages.add(DataStreamMessage(data)); }).asFuture(); + final sendTimeout = options.sendTimeout; + if (sendTimeout != null) { + await requestStreamFuture.timeout(sendTimeout, onTimeout: () { + throw DioException.sendTimeout( + timeout: sendTimeout, + requestOptions: options, + ); + }); + } else { + await requestStreamFuture; + } } - await stream.outgoingMessages.close(); final sc = StreamController(); @@ -145,7 +155,21 @@ class Http2Adapter implements HttpClientAdapter { cancelOnError: true, ); - await completer.future; + final receiveTimeout = options.receiveTimeout; + if (receiveTimeout != null) { + await completer.future.timeout( + receiveTimeout, + onTimeout: () { + subscription.cancel().whenComplete(() => sc.close()); + throw DioException.receiveTimeout( + timeout: receiveTimeout, + requestOptions: options, + ); + }, + ); + } else { + await completer.future; + } // Handle redirection if (needRedirect) { diff --git a/plugins/http2_adapter/test/http2_test.dart b/plugins/http2_adapter/test/http2_test.dart index 9ec303fa3..49181939e 100644 --- a/plugins/http2_adapter/test/http2_test.dart +++ b/plugins/http2_adapter/test/http2_test.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:dio/dio.dart'; import 'package:dio_http2_adapter/dio_http2_adapter.dart'; import 'package:test/test.dart'; @@ -76,4 +79,40 @@ void main() { final res = await dio.post('post', data: 'TEST'); expect(res.data.toString(), contains('TEST')); }); + + + test('catch DioException when receiveTimeout', () { + final dio = Dio() + ..options.baseUrl = 'https://httpbun.com/' + ..httpClientAdapter = Http2Adapter( + ConnectionManager( + idleTimeout: Duration(milliseconds: 10), + ), + ); + dio.options.receiveTimeout = Duration(seconds: 5); + + expectLater( + dio.get('/drip?delay=10&numbytes=1'), + allOf([ + throwsA(isA()), + throwsA(predicate( + (DioException e) => e.type == DioExceptionType.receiveTimeout)), + throwsA(predicate( + (DioException e) => e.message!.contains('0:00:05.000000'))), + ]), + ); + }, testOn: 'vm'); + + test('no DioException when receiveTimeout > request duration', () async { + final dio = Dio() + ..options.baseUrl = 'https://httpbun.com/' + ..httpClientAdapter = Http2Adapter( + ConnectionManager( + idleTimeout: Duration(milliseconds: 10), + ), + ); + dio.options.receiveTimeout = Duration(seconds: 5); + + await dio.get('/drip?delay=1&numbytes=1'); + }); } diff --git a/plugins/http2_adapter/test/mock/flutter.png b/plugins/http2_adapter/test/mock/flutter.png new file mode 100644 index 0000000000000000000000000000000000000000..a82ca4352881e942570b90991ce27944434bf9bd GIT binary patch literal 1529 zcmVto9EqS_vad5j1KB5;F!KL)+~AbF}RMFLj`;#~3(v04R3= zFNgp$lmZMb4n2koD_jdSbqg?R4nK`5iPHcWMg}l%040L}J)N&$;hO*e1nx;hK~#90 z?cC{78bJ`iaa_cBfCp$i63L;-5=E0}G#=!7|Ch?_!OG&W%*;0SQQZ#!^{ei`yB{nX z4a8Y*ErHGQ8m44_S&C#`j6`>0n~v^8C8g2g=}EMB+sN68N>s8q32yPa6P2jsWhGiX zLCLO4RI(^ZM~noy_zs{%&Q4T9PJ&##oKECOv=h-1EnX#4679q?a^8hIo2U{woj^)( zcLG_w7>O1SB$3mJ9Elch8RSmnNaS=PN20}B2Dg(JV{DPB$|awJ+jL5UV` z87&@2BBv8M5-r{`T0D?MPA76CTD)a&JJ}T@k<-bp9Eq6X<#ZxQBBv8M5-r{`T0D?M zPA76CTD)aC>nZ_}Kz5=M7zt$YVkBBTkVH-=awJ;3WpF!L6C=@1RHBmk16e$>L`?CZ zJK0x>N`y)vi`Si~L?zTEFJJuL!3x+MJpJqr!cO*DaI<`XD*^n<6_r$fEP`a>x)6$A zkc9b8a;0Oj4tL za+2V^5R?R3NJ@e%L?rm z6P8(f@#thcb$h9^lL||Kq^_l|WQN86x@ueMORSbaiJ4#vgv8Q*xFpz8ulNZ|c`reo z)Vnb#$&{s*B+LSHUC%6at}F2Q0x6m3rM|@50(V^{i=*UYmBh1sXi0u!FcPm`;3erl zg9Ax?EU=vvmJXD}y_dyE8jY`T5=YA-uFHKKB~I-#kvR0iMKa&QM>6lmkR@NdB$XCc zJE^p=lg#XepJbMWqoiqlRx&HT!opVatbL}}l}@=A&XTf)@pWZW^uk_JSOiG&_IYQZRaduugC1Pob2S$=D<%z=5GM1Dmk(n+xOCrgaQr-$XOFS5yoD_e)$`oEV z7N3omGDV3ZW)>sKmSW!uBTIZfxs*vtG#)0sB$7cYc}97{u&|_(;XDe58#9u;lxoT< zc}skmEG4}!8zqX^SQ5$jzNRVdNeKB~jKoecZ^qLQ+Ll66q9iXa76*m)H)mPmi;OZ% z-tKcs@`!AkFzghnz2uT9%0_Q>hJ%E~x|jIpyW+kwO8{+#!#fWK4a-FPh2;GHuD9wd zG4p|nCA%&|c^El`ZW-+MbBmFfIe#1lA@U({V~OPQ@ovVeiR)14#dLE2P=0+6vvg09 zEx&&M8I5`X6w^MkT}d2YoNvi+BtAjcNPVLEnq^BHn_2W7@uT(D$Ofh_|5Mn6_^n^u1^= fG%f$&EZx)pulo@}90TIQ00000NkvXXu0mjf=w7}x literal 0 HcmV?d00001