send method
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<http.StreamedResponse> send(http.BaseRequest request) async {
final client = activeInnerClient;
final key = request.url.toString();
// Handle GET request caching
if (request.method == 'GET') {
final raw = _box.get(key);
if (raw != null) {
final cached = CachedHttpResponse.fromMap(raw is Map ? raw : {});
if (!cached.isExpired(ttl)) {
stats.hits++;
// Refresh position in Hive box to preserve LRU order
await _box.delete(key);
await _box.put(key, raw);
onCacheHit?.call(key, stats);
return http.StreamedResponse(
Stream.value(cached.bytes),
cached.statusCode,
headers: cached.headers,
contentLength: cached.bytes.length,
request: request,
);
} else {
await _removeKey(key);
}
}
stats.misses++;
}
final response = await client.send(request);
// Cache successful GET responses
if (request.method == 'GET' && response.statusCode == 200) {
final bytes = await response.stream.toBytes();
await _putInCache(
key,
CachedHttpResponse(
bytes: bytes,
statusCode: response.statusCode,
headers: response.headers,
createdAt: DateTime.now(),
),
);
return http.StreamedResponse(
Stream.value(bytes),
response.statusCode,
headers: response.headers,
contentLength: response.contentLength,
reasonPhrase: response.reasonPhrase,
isRedirect: response.isRedirect,
persistentConnection: response.persistentConnection,
request: response.request,
);
}
// Check if the request is a mutating operation
if (isMutatingRequest(request) && response.statusCode < 400) {
await _applyInvalidation(request);
}
return response;
}