Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/lib/search/search_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ SearchClient get searchClient => ss.lookup(#_searchClient) as SearchClient;
/// indexed data.
class SearchClient {
/// The HTTP client used for making calls to our search service.
final _httpClient = httpRetryClient();
final _httpClient = httpRenewableClient();

/// Before this timestamp we may use the fallback search service URL, which
/// is the unversioned service URL, potentially getting responses from an
Expand Down
65 changes: 65 additions & 0 deletions pkg/_pub_shared/lib/utils/http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ Future<K> httpGetWithRetry<K>(
},
maxAttempts: maxAttempts,
retryIf: (e) => _retryIf(e) || (retryIf != null && retryIf(e)),
onRetry: (_) {
if (client is _RenewableClient) {
client.renew();
}
},
);
}

Expand Down Expand Up @@ -113,3 +118,63 @@ class UnexpectedStatusException implements Exception {
@override
String toString() => 'UnexpectedStatusException: $message';
}

/// Creates a client that will be renewed when a HTTP retry happens.
http.Client httpRenewableClient() => _RenewableClient();

class _RenewableClient extends http.BaseClient {
var _client = _Client(http.Client());

@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
return await _client.send(request);
}

void renew() {
final c = _client;
_client = _Client(http.Client());
c.close();
}

@override
void close() {
_client.close();
}
}

class _Client extends http.BaseClient {
final http.Client _client;
final _pending = <Future>[];
var _closing = false;

_Client(this._client);

@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
if (_closing) {
throw StateError('HTTP client is closed.');
}
final f = _client.send(request);
_pending.add(f);
try {
return await f;
} finally {
_pending.remove(f);
if (_closing && _pending.isEmpty) {
_client.close();
}
}
}

@override
void close() {
_closing = true;
if (_pending.isEmpty) {
_client.close();
return;
}
unawaited(Future.delayed(Duration(minutes: 1), () {
_client.close();
}));
}
}