send method
Future<StreamedResponse>
send(
- BaseRequest request, {
- CancellationToken? cancellationToken,
override
Sends an HTTP request and asynchronously returns the response.
Implementers should call BaseRequest.finalize to get the body of the request as a ByteStream. They shouldn't make any assumptions about the state of the stream; it could have data written to it asynchronously at a later point, or it could already be closed when it's returned. Any internal HTTP errors should be wrapped as ClientExceptions.
Implementation
@override
Future<StreamedResponse> send(
BaseRequest request, {
CancellationToken? cancellationToken,
}) async {
final splitter = StreamSplitter(request.finalize());
var i = 0;
for (;;) {
StreamedResponse? response;
try {
response = await _inner.send(
_copyRequest(request, splitter.split()),
cancellationToken: cancellationToken,
);
} catch (error, stackTrace) {
// Always rethrow cancellation errors
if (cancellationToken?.isCancelled == true &&
error == cancellationToken?.exception) rethrow;
// Rethrow if this is the last attempt
if (i == _retries) rethrow;
// Rethrow if _whenError returns false, indicating we shouldn't retry
if (!await CancellableFuture.from(
() => _whenError(error, stackTrace),
cancellationToken,
)) rethrow;
}
if (response != null) {
if (i == _retries ||
!await CancellableFuture.from(
() => _when(response!),
cancellationToken,
)) return response;
// Make sure the response stream is listened to so that we don't leave
// dangling connections.
_unawaited(response.stream.listen((_) {}).cancel().catchError((_) {}));
}
await CancellableFuture.delayed<void>(_delay(i), cancellationToken);
if (_onRetry != null) {
await CancellableFuture.from(
() => _onRetry.call(request, response, i),
cancellationToken,
);
}
i++;
}
}