Add very short term (2 second) HTTP GET request cache for when multiple calls to same endpoint from redraws

main
HankG 2023-11-29 14:44:37 -06:00 zatwierdzone przez Hank Grabowski
rodzic 782d3ba14d
commit 4ff673e6fd
1 zmienionych plików z 135 dodań i 10 usunięć

Wyświetl plik

@ -12,6 +12,133 @@ final _logger = Logger('NetworkUtils');
http.Response requestTimeout() => http.Response('Client side timeout', 408);
enum _RequestType {
get,
}
const _expireDuration = Duration(seconds: 2);
class _CachedResponse {
final _RequestType requestType;
final Uri requestUri;
final Map<String, dynamic> requestBody;
final Map<String, String> headers;
final http.Response response;
final DateTime requestTime;
_CachedResponse(
{required this.requestType,
required this.requestUri,
required this.requestBody,
required this.headers,
required this.response,
required this.requestTime});
factory _CachedResponse.requestStub(_RequestType type, Uri uri,
Map<String, String>? headers, Map<String, dynamic>? body) =>
_CachedResponse(
requestType: type,
requestUri: uri,
requestBody: body ?? {},
headers: headers ?? {},
response: http.Response('', 555),
requestTime: DateTime(0),
);
factory _CachedResponse.response(
_RequestType type,
Uri uri,
Map<String, String>? headers,
Map<String, dynamic>? body,
http.Response response,
) =>
_CachedResponse(
requestType: type,
requestUri: uri,
requestBody: body ?? {},
headers: headers ?? {},
response: response,
requestTime: DateTime.now(),
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is _CachedResponse &&
runtimeType == other.runtimeType &&
requestType == other.requestType &&
requestUri == other.requestUri;
@override
int get hashCode => requestType.hashCode ^ requestUri.hashCode;
}
class _ExpiringRequestCache {
final _responses = <_CachedResponse, _CachedResponse>{};
void _cleanupCache() {
final expireTime = DateTime.now().subtract(_expireDuration);
_logger
.finest('Cleaning up request cache with ${_responses.length} entries');
_responses.removeWhere((key, value) {
final expired = key.requestTime.isBefore(expireTime);
if (expired) {
_logger.finest(
'Expiring request: ${value.requestType} => ${value.requestUri}');
}
return expired;
});
_logger.finest('Cleaned up request cache has ${_responses.length} entries');
}
Future<http.Response> getRequestOrExecute({
required Uri url,
Map<String, String>? headers,
Map<String, dynamic>? body,
Duration? timeout,
}) async {
_cleanupCache();
const type = _RequestType.get;
final requestStub = _CachedResponse.requestStub(
type,
url,
headers,
null,
);
late final http.Response response;
if (_responses.containsKey(requestStub)) {
print('Returning cached response for $type => $url');
response = _responses[requestStub]?.response ?? http.Response('', 555);
} else {
final request = http.get(
url,
headers: headers,
);
response = await request.timeout(
timeout ?? apiCallTimeout,
onTimeout: requestTimeout,
);
final cacheEntry = _CachedResponse.response(
type,
url,
headers ?? {},
body ?? {},
response,
);
print('Adding cached response for $type => $url');
_responses[cacheEntry] = cacheEntry;
}
return response;
}
}
final _cache = _ExpiringRequestCache();
FutureResult<PagedResponse<String>, ExecError> getUrl(
Uri url, {
Map<String, String>? headers,
@ -23,14 +150,10 @@ FutureResult<PagedResponse<String>, ExecError> getUrl(
requestHeaders['Cookie'] = 'XDEBUG_SESSION=PHPSTORM;path=/';
}
try {
final request = http.get(
url,
headers: requestHeaders,
);
final response = await request.timeout(
timeout ?? apiCallTimeout,
onTimeout: requestTimeout,
final response = await _cache.getRequestOrExecute(
url: url,
headers: headers,
timeout: timeout ?? apiCallTimeout,
);
if (response.statusCode != 200) {
@ -87,6 +210,7 @@ FutureResult<String, ExecError> putUrl(
Uri url,
Map<String, dynamic> body, {
Map<String, String>? headers,
Duration? timeout,
}) async {
_logger.fine('PUT: $url \n Body: $body');
try {
@ -97,7 +221,7 @@ FutureResult<String, ExecError> putUrl(
);
final response = await request.timeout(
apiCallTimeout,
timeout ?? apiCallTimeout,
onTimeout: requestTimeout,
);
@ -117,6 +241,7 @@ FutureResult<String, ExecError> deleteUrl(
Uri url,
Map<String, dynamic> body, {
Map<String, String>? headers,
Duration? timeout,
}) async {
_logger.fine('DELETE: $url');
try {
@ -127,7 +252,7 @@ FutureResult<String, ExecError> deleteUrl(
);
final response = await request.timeout(
apiCallTimeout,
timeout ?? apiCallTimeout,
onTimeout: requestTimeout,
);