diff --git a/plugins/http2_adapter/CHANGELOG.md b/plugins/http2_adapter/CHANGELOG.md index 039c8a1f0..3b49c2e88 100644 --- a/plugins/http2_adapter/CHANGELOG.md +++ b/plugins/http2_adapter/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -*None.* +- Implement `sendTimeout` and `receiveTimeout` for the adapter. ## 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..6aa4ed822 100644 --- a/plugins/http2_adapter/lib/src/http2_adapter.dart +++ b/plugins/http2_adapter/lib/src/http2_adapter.dart @@ -89,11 +89,24 @@ 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 +158,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..b3a777c58 100644 --- a/plugins/http2_adapter/test/http2_test.dart +++ b/plugins/http2_adapter/test/http2_test.dart @@ -76,4 +76,45 @@ void main() { final res = await dio.post('post', data: 'TEST'); expect(res.data.toString(), contains('TEST')); }); + + test('catch DioException when receiveTimeout', () { + final dio = Dio( + BaseOptions( + baseUrl: 'https://httpbun.com/', + receiveTimeout: Duration(seconds: 5), + ), + ); + dio.httpClientAdapter = Http2Adapter( + ConnectionManager( + idleTimeout: Duration(milliseconds: 10), + ), + ); + + 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'))), + ]), + ); + }); + + test('no DioException when receiveTimeout > request duration', () async { + final dio = Dio( + BaseOptions( + baseUrl: 'https://httpbun.com/', + receiveTimeout: Duration(seconds: 5), + ), + ); + dio.httpClientAdapter = Http2Adapter( + ConnectionManager( + idleTimeout: Duration(milliseconds: 10), + ), + ); + + await dio.get('/drip?delay=1&numbytes=1'); + }); }