mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Auto merge of #18676 - gterzian:continue_http_cache_work, r=jdm
Continue http cache work <!-- Please describe your changes on the following line: --> Work in progress, and not quite worth a review yet. (Continuation of https://github.com/servo/servo/pull/4117) TODO - [ ] cache metadata (find some subset of`net_traits::Metadata` that can be shared across threads, it seems the problem is mainly stuff inside `hyper::header` in the `headers` field) - [ ] determine which other fields of a `Response` need to be cached, so a full and valid one can be returned upon a cache hit. - [ ] determine how to best share the cache across fetch threads (inside HttpState like I tried now?) - [ ] Spend more time reading the spec and make sure the cache follows it where it matters. - [ ] Make the current wpt tests pass. - [ ] More... --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #12972 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18676) <!-- Reviewable:end -->
This commit is contained in:
commit
e2bc0f017c
37 changed files with 1238 additions and 631 deletions
|
@ -13,6 +13,7 @@ use fetch::methods::{Data, DoneChannel, FetchContext, Target};
|
|||
use fetch::methods::{is_cors_safelisted_request_header, is_cors_safelisted_method, main_fetch};
|
||||
use flate2::read::{DeflateDecoder, GzDecoder};
|
||||
use hsts::HstsList;
|
||||
use http_cache::HttpCache;
|
||||
use hyper::Error as HttpError;
|
||||
use hyper::LanguageTag;
|
||||
use hyper::client::{Pool, Request as HyperRequest, Response as HyperResponse};
|
||||
|
@ -22,7 +23,7 @@ use hyper::header::{AccessControlMaxAge, AccessControlRequestHeaders};
|
|||
use hyper::header::{AccessControlRequestMethod, AcceptEncoding, AcceptLanguage};
|
||||
use hyper::header::{Authorization, Basic, CacheControl, CacheDirective};
|
||||
use hyper::header::{ContentEncoding, ContentLength, Encoding, Header, Headers};
|
||||
use hyper::header::{Host, Origin as HyperOrigin, IfMatch, IfRange};
|
||||
use hyper::header::{Host, HttpDate, Origin as HyperOrigin, IfMatch, IfRange};
|
||||
use hyper::header::{IfUnmodifiedSince, IfModifiedSince, IfNoneMatch, Location};
|
||||
use hyper::header::{Pragma, Quality, QualityItem, Referer, SetCookie};
|
||||
use hyper::header::{UserAgent, q, qitem};
|
||||
|
@ -45,6 +46,7 @@ use std::io::{self, Read, Write};
|
|||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::thread;
|
||||
|
@ -69,6 +71,7 @@ fn read_block<R: Read>(reader: &mut R) -> Result<Data, ()> {
|
|||
pub struct HttpState {
|
||||
pub hsts_list: RwLock<HstsList>,
|
||||
pub cookie_jar: RwLock<CookieStorage>,
|
||||
pub http_cache: RwLock<HttpCache>,
|
||||
pub auth_cache: RwLock<AuthCache>,
|
||||
pub ssl_client: OpensslClient,
|
||||
pub connector: Pool<Connector>,
|
||||
|
@ -80,6 +83,7 @@ impl HttpState {
|
|||
hsts_list: RwLock::new(HstsList::new()),
|
||||
cookie_jar: RwLock::new(CookieStorage::new(150)),
|
||||
auth_cache: RwLock::new(AuthCache::new()),
|
||||
http_cache: RwLock::new(HttpCache::new()),
|
||||
ssl_client: ssl_client.clone(),
|
||||
connector: create_http_connector(ssl_client),
|
||||
}
|
||||
|
@ -895,34 +899,35 @@ fn http_network_or_cache_fetch(request: &mut Request,
|
|||
let mut revalidating_flag = false;
|
||||
|
||||
// Step 21
|
||||
// TODO have a HTTP cache to check for a completed response
|
||||
let complete_http_response_from_cache: Option<Response> = None;
|
||||
if http_request.cache_mode != CacheMode::NoStore &&
|
||||
http_request.cache_mode != CacheMode::Reload &&
|
||||
complete_http_response_from_cache.is_some() {
|
||||
// TODO Substep 1 and 2. Select a response from HTTP cache.
|
||||
if let Ok(http_cache) = context.state.http_cache.read() {
|
||||
if let Some(response_from_cache) = http_cache.construct_response(&http_request) {
|
||||
let response_headers = response_from_cache.response.headers.clone();
|
||||
// Substep 1, 2, 3, 4
|
||||
let (cached_response, needs_revalidation) = match (http_request.cache_mode, &http_request.mode) {
|
||||
(CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
|
||||
(CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => (Some(response_from_cache.response), false),
|
||||
(CacheMode::OnlyIfCached, _) | (CacheMode::NoStore, _) | (CacheMode::Reload, _) => (None, false),
|
||||
(_, _) => (Some(response_from_cache.response), response_from_cache.needs_validation)
|
||||
};
|
||||
if needs_revalidation {
|
||||
revalidating_flag = true;
|
||||
// Substep 5
|
||||
// TODO: find out why the typed header getter return None from the headers of cached responses.
|
||||
if let Some(date_slice) = response_headers.get_raw("Last-Modified") {
|
||||
let date_string = String::from_utf8_lossy(&date_slice[0]);
|
||||
if let Ok(http_date) = HttpDate::from_str(&date_string) {
|
||||
http_request.headers.set(IfModifiedSince(http_date));
|
||||
}
|
||||
}
|
||||
if let Some(entity_tag) =
|
||||
response_headers.get_raw("ETag") {
|
||||
http_request.headers.set_raw("If-None-Match", entity_tag.to_vec());
|
||||
|
||||
// Substep 3
|
||||
if let Some(ref response) = response {
|
||||
revalidating_flag = response_needs_revalidation(&response);
|
||||
};
|
||||
|
||||
// Substep 4
|
||||
if http_request.cache_mode == CacheMode::ForceCache ||
|
||||
http_request.cache_mode == CacheMode::OnlyIfCached {
|
||||
// TODO pull response from HTTP cache
|
||||
// response = http_request
|
||||
}
|
||||
|
||||
if revalidating_flag {
|
||||
// Substep 5
|
||||
// TODO set If-None-Match and If-Modified-Since according to cached
|
||||
// response headers.
|
||||
} else {
|
||||
// Substep 6
|
||||
// TODO pull response from HTTP cache
|
||||
// response = http_request
|
||||
// response.cache_state = CacheState::Local;
|
||||
}
|
||||
} else {
|
||||
// Substep 6
|
||||
response = cached_response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -933,26 +938,37 @@ fn http_network_or_cache_fetch(request: &mut Request,
|
|||
return Response::network_error(
|
||||
NetworkError::Internal("Couldn't find response in cache".into()))
|
||||
}
|
||||
}
|
||||
// More Step 22
|
||||
if response.is_none() {
|
||||
// Substep 2
|
||||
let forward_response = http_network_fetch(http_request, credentials_flag,
|
||||
done_chan, context);
|
||||
// Substep 3
|
||||
if let Some((200...399, _)) = forward_response.raw_status {
|
||||
if !http_request.method.safe() {
|
||||
// TODO Invalidate HTTP cache response
|
||||
if let Ok(mut http_cache) = context.state.http_cache.write() {
|
||||
http_cache.invalidate(&http_request, &forward_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Substep 4
|
||||
if revalidating_flag && forward_response.status.map_or(false, |s| s == StatusCode::NotModified) {
|
||||
// TODO update forward_response headers with cached response headers
|
||||
if let Ok(mut http_cache) = context.state.http_cache.write() {
|
||||
response = http_cache.refresh(&http_request, forward_response.clone(), done_chan);
|
||||
}
|
||||
}
|
||||
|
||||
// Substep 5
|
||||
if response.is_none() {
|
||||
if http_request.cache_mode != CacheMode::NoStore {
|
||||
// Subsubstep 2, doing it first to avoid a clone of forward_response.
|
||||
if let Ok(mut http_cache) = context.state.http_cache.write() {
|
||||
http_cache.store(&http_request, &forward_response);
|
||||
}
|
||||
}
|
||||
// Subsubstep 1
|
||||
response = Some(forward_response);
|
||||
// Subsubstep 2
|
||||
// TODO: store http_request and forward_response in cache
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1170,7 +1186,9 @@ fn http_network_fetch(request: &Request,
|
|||
|
||||
// Step 14
|
||||
if !response.is_network_error() && request.cache_mode != CacheMode::NoStore {
|
||||
// TODO update response in the HTTP cache for request
|
||||
if let Ok(mut http_cache) = context.state.http_cache.write() {
|
||||
http_cache.store(&request, &response);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this step isn't possible yet
|
||||
|
@ -1368,11 +1386,6 @@ fn is_no_store_cache(headers: &Headers) -> bool {
|
|||
headers.has::<IfRange>()
|
||||
}
|
||||
|
||||
fn response_needs_revalidation(_response: &Response) -> bool {
|
||||
// TODO this function
|
||||
false
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#redirect-status>
|
||||
pub fn is_redirect_status(status: StatusCode) -> bool {
|
||||
match status {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue