Skip to content

Commit 4145098

Browse files
committed
HTTP Client that can be renewed / reset after a retry.
1 parent b4cf93d commit 4145098

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

app/lib/search/search_client.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ SearchClient get searchClient => ss.lookup(#_searchClient) as SearchClient;
3232
/// indexed data.
3333
class SearchClient {
3434
/// The HTTP client used for making calls to our search service.
35-
final _httpClient = httpRetryClient();
35+
final _httpClient = httpRenewableClient();
3636

3737
/// Before this timestamp we may use the fallback search service URL, which
3838
/// is the unversioned service URL, potentially getting responses from an

pkg/_pub_shared/lib/utils/http.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ Future<K> httpGetWithRetry<K>(
8383
},
8484
maxAttempts: maxAttempts,
8585
retryIf: (e) => _retryIf(e) || (retryIf != null && retryIf(e)),
86+
onRetry: (_) {
87+
if (client is _RenewableClient) {
88+
client.renew();
89+
}
90+
},
8691
);
8792
}
8893

@@ -113,3 +118,63 @@ class UnexpectedStatusException implements Exception {
113118
@override
114119
String toString() => 'UnexpectedStatusException: $message';
115120
}
121+
122+
/// Creates a client that will be renewed when a HTTP retry happens.
123+
http.Client httpRenewableClient() => _RenewableClient();
124+
125+
class _RenewableClient extends http.BaseClient {
126+
var _client = _Client(http.Client());
127+
128+
@override
129+
Future<http.StreamedResponse> send(http.BaseRequest request) async {
130+
return await _client.send(request);
131+
}
132+
133+
void renew() {
134+
final c = _client;
135+
_client = _Client(http.Client());
136+
c.close();
137+
}
138+
139+
@override
140+
void close() {
141+
_client.close();
142+
}
143+
}
144+
145+
class _Client extends http.BaseClient {
146+
final http.Client _client;
147+
final _pending = <Future>[];
148+
var _closing = false;
149+
150+
_Client(this._client);
151+
152+
@override
153+
Future<http.StreamedResponse> send(http.BaseRequest request) async {
154+
if (_closing) {
155+
throw StateError('HTTP client is closed.');
156+
}
157+
final f = _client.send(request);
158+
_pending.add(f);
159+
try {
160+
return await f;
161+
} finally {
162+
_pending.remove(f);
163+
if (_closing && _pending.isEmpty) {
164+
_client.close();
165+
}
166+
}
167+
}
168+
169+
@override
170+
void close() {
171+
_closing = true;
172+
if (_pending.isEmpty) {
173+
_client.close();
174+
return;
175+
}
176+
unawaited(Future.delayed(Duration(minutes: 1), () {
177+
_client.close();
178+
}));
179+
}
180+
}

0 commit comments

Comments
 (0)