0.35.5
This commit is contained in:
1 parent
ff42a13dbf
commit
e7b4991316
14 files changed
+176
-50
No files matched your search
Vendored
+39
-2
@@ -6,7 +6,6 @@ mod tests {
|
||||
h.insert("cache-control", value.parse().unwrap());
|
||||
h
|
||||
}
|
||||
// Legacy policy; varnish-compatible behavior is tested separately.
|
||||
fn settings() -> Settings {
|
||||
Settings {
|
||||
enabled: true,
|
||||
@@ -1559,7 +1558,10 @@ mod tests {
|
||||
let second = cache
|
||||
.ticket(1, "http", "example.test", "app", "/second", &headers)
|
||||
.unwrap();
|
||||
fill(&cache, first.clone(), "first", &s).await;
|
||||
// Stored bodies are trimmed to their real size, so the first object must
|
||||
// itself be large for the next fill reservation to need its space.
|
||||
let large: &'static str = Box::leak("f".repeat(60 * 1024).into_boxed_str());
|
||||
fill(&cache, first.clone(), large, &s).await;
|
||||
assert!(cache.get(&first).is_some());
|
||||
let before = cache.view();
|
||||
assert!(before.hot_allocated_bytes <= s.max_bytes);
|
||||
@@ -1821,4 +1823,39 @@ mod tests {
|
||||
assert_eq!(cache.view().entries, 0);
|
||||
assert_eq!(cache.view().fills_active, 0);
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn stored_entries_do_not_pin_origin_buffers_or_spare_capacity() {
|
||||
let cache = Cache::new(1);
|
||||
let s = settings();
|
||||
let t = ticket(&cache, &HeaderMap::new());
|
||||
// Header values parsed by hyper are slices of a larger read buffer.
|
||||
let read_buffer = Bytes::from(vec![b'x'; 64 * 1024]);
|
||||
let mut h = headers("max-age=60");
|
||||
h.insert(
|
||||
"x-origin",
|
||||
HeaderValue::from_maybe_shared(read_buffer.slice(0..16)).unwrap(),
|
||||
);
|
||||
h.insert(
|
||||
::http::header::ETAG,
|
||||
HeaderValue::from_maybe_shared(read_buffer.slice(16..24)).unwrap(),
|
||||
);
|
||||
let p = plan(StatusCode::OK, &h, &s, Duration::ZERO).unwrap();
|
||||
let mut response = Response::new(Body::from("small body"));
|
||||
*response.headers_mut() = h;
|
||||
cache
|
||||
.wrap(response, t.clone(), p, &s)
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(cache.get(&t).is_some());
|
||||
assert!(
|
||||
read_buffer.try_into_mut().is_ok(),
|
||||
"cached headers must not keep the origin read buffer alive"
|
||||
);
|
||||
let store = cache.store.lock().unwrap();
|
||||
let entry = store.entries.values().next().expect("stored object");
|
||||
assert_eq!(entry.body.len(), 10);
|
||||
assert!(entry.body_allocated <= 10 + 10 / 8 + 1);
|
||||
}
|
||||
}
|
||||
Vendored
+40
-2
@@ -1,3 +1,16 @@
|
||||
fn detached_header_value(value: &HeaderValue) -> HeaderValue {
|
||||
let mut detached = HeaderValue::from_bytes(value.as_bytes())
|
||||
.expect("existing HTTP header value must remain valid when copied");
|
||||
detached.set_sensitive(value.is_sensitive());
|
||||
detached
|
||||
}
|
||||
|
||||
fn detach_header_map(headers: &mut HeaderMap) {
|
||||
for value in headers.values_mut() {
|
||||
*value = detached_header_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
struct ByteGaugeGuard<'a> {
|
||||
counter: &'a AtomicU64,
|
||||
bytes: u64,
|
||||
@@ -102,6 +115,9 @@ impl Candidate {
|
||||
let (body, body_allocation) = if let Some(body) = self.shared.take() {
|
||||
(body, self.shared_allocated)
|
||||
} else {
|
||||
if self.data.capacity() > self.data.len().saturating_add(self.data.len() / 8) {
|
||||
self.data.shrink_to_fit();
|
||||
}
|
||||
let allocated = self.data.capacity().max(self.data.len());
|
||||
(Bytes::from(std::mem::take(&mut self.data)), allocated)
|
||||
};
|
||||
@@ -111,6 +127,12 @@ impl Candidate {
|
||||
let domain = self.ticket.domain.clone();
|
||||
let path = self.ticket.path.clone();
|
||||
let target = self.ticket.target.clone();
|
||||
detach_header_map(&mut self.headers);
|
||||
self.origin_etag = self.origin_etag.as_ref().map(detached_header_value);
|
||||
self.origin_last_modified = self
|
||||
.origin_last_modified
|
||||
.as_ref()
|
||||
.map(detached_header_value);
|
||||
let tags = cache_tags(&self.headers);
|
||||
let cost = hot_entry_cost(
|
||||
body_allocation,
|
||||
@@ -226,7 +248,21 @@ impl HttpBody for FillBody {
|
||||
let needed = candidate.data.len().saturating_add(data.len());
|
||||
if needed <= candidate.settings.max_object_bytes {
|
||||
let before = candidate.data.capacity();
|
||||
let required_growth = needed.saturating_sub(before);
|
||||
let target_capacity = if needed <= before {
|
||||
before
|
||||
} else if let Some(length) =
|
||||
candidate.plan.expected_length.filter(|length| {
|
||||
*length >= needed
|
||||
&& *length <= candidate.settings.max_object_bytes
|
||||
})
|
||||
{
|
||||
length
|
||||
} else {
|
||||
needed
|
||||
.max(before.saturating_mul(2))
|
||||
.min(candidate.settings.max_object_bytes)
|
||||
};
|
||||
let required_growth = target_capacity.saturating_sub(before);
|
||||
if required_growth > 0
|
||||
&& !candidate
|
||||
.fill_gauge
|
||||
@@ -246,7 +282,9 @@ impl HttpBody for FillBody {
|
||||
} else if required_growth > 0
|
||||
&& candidate
|
||||
.data
|
||||
.try_reserve_exact(needed.saturating_sub(candidate.data.len()))
|
||||
.try_reserve_exact(
|
||||
target_capacity.saturating_sub(candidate.data.len()),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
candidate.cache.record_reason(
|
||||
|
||||
Reference in new issue
Block a user