mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
libservo: Convert intercept_web_resource_load
into load_web_resource
(#35564)
Rework the `WebViewDelegate::intercept_web_resource_load` into `WebViewDelegate::load_web_resource` and clean up internal messaging. The main thing here is adding objects which manage the response to these delegate methods. Now we have `WebResourceLoad` and `InterceptedWebResourceLoad` which make it much harder to misuse the API. In addition, the internal messaging for this is cleaned up. Canceling and finishing the load are unrelated to the HTTP body so they are no longer subtypes of an HttpBodyData message. Processing of messages is made a bit more efficient by collecting all body chunks in a vector and only flattening the chunks at the end. Finally, "interceptor" is a much more common spelling than "intercepter" so I've gone ahead and made this change everywhere. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
f6e2e3d418
commit
cf2b93f18a
14 changed files with 245 additions and 208 deletions
|
@ -43,7 +43,7 @@ use crate::fetch::headers::determine_nosniff;
|
|||
use crate::filemanager_thread::FileManager;
|
||||
use crate::http_loader::{determine_requests_referrer, http_fetch, set_default_accept, HttpState};
|
||||
use crate::protocols::ProtocolRegistry;
|
||||
use crate::request_intercepter::RequestIntercepter;
|
||||
use crate::request_interceptor::RequestInterceptor;
|
||||
use crate::subresource_integrity::is_response_integrity_valid;
|
||||
|
||||
pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send);
|
||||
|
@ -61,7 +61,7 @@ pub struct FetchContext {
|
|||
pub devtools_chan: Option<Arc<Mutex<Sender<DevtoolsControlMsg>>>>,
|
||||
pub filemanager: Arc<Mutex<FileManager>>,
|
||||
pub file_token: FileTokenCheck,
|
||||
pub request_intercepter: Arc<Mutex<RequestIntercepter>>,
|
||||
pub request_interceptor: Arc<Mutex<RequestInterceptor>>,
|
||||
pub cancellation_listener: Arc<CancellationListener>,
|
||||
pub timing: ServoArc<Mutex<ResourceFetchTiming>>,
|
||||
pub protocols: Arc<ProtocolRegistry>,
|
||||
|
@ -328,7 +328,7 @@ pub async fn main_fetch(
|
|||
|
||||
// Intercept the request and maybe override the response.
|
||||
context
|
||||
.request_intercepter
|
||||
.request_interceptor
|
||||
.lock()
|
||||
.unwrap()
|
||||
.intercept_request(request, &mut response, context);
|
||||
|
|
|
@ -18,7 +18,7 @@ pub mod image_cache;
|
|||
pub mod local_directory_listing;
|
||||
pub mod mime_classifier;
|
||||
pub mod protocols;
|
||||
pub mod request_intercepter;
|
||||
pub mod request_interceptor;
|
||||
pub mod resource_thread;
|
||||
mod storage_thread;
|
||||
pub mod subresource_integrity;
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use content_security_policy::Destination;
|
||||
use embedder_traits::{
|
||||
EmbedderMsg, EmbedderProxy, HttpBodyData, WebResourceRequest, WebResourceResponseMsg,
|
||||
};
|
||||
use ipc_channel::ipc;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use net_traits::NetworkError;
|
||||
|
||||
use crate::fetch::methods::FetchContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RequestIntercepter {
|
||||
embedder_proxy: EmbedderProxy,
|
||||
}
|
||||
|
||||
impl RequestIntercepter {
|
||||
pub fn new(embedder_proxy: EmbedderProxy) -> RequestIntercepter {
|
||||
RequestIntercepter { embedder_proxy }
|
||||
}
|
||||
|
||||
pub fn intercept_request(
|
||||
&self,
|
||||
request: &mut Request,
|
||||
response: &mut Option<Response>,
|
||||
context: &FetchContext,
|
||||
) {
|
||||
let (tx, rx) = ipc::channel().unwrap();
|
||||
let is_for_main_frame = matches!(request.destination, Destination::Document);
|
||||
let req = WebResourceRequest::new(
|
||||
request.method.clone(),
|
||||
request.headers.clone(),
|
||||
request.url(),
|
||||
is_for_main_frame,
|
||||
request.redirect_count > 0,
|
||||
);
|
||||
|
||||
self.embedder_proxy.send(EmbedderMsg::WebResourceRequested(
|
||||
request.target_webview_id,
|
||||
req,
|
||||
tx,
|
||||
));
|
||||
let mut response_received = false;
|
||||
|
||||
// TODO: use done_chan and run in CoreResourceThreadPool.
|
||||
while let Ok(msg) = rx.recv() {
|
||||
match msg {
|
||||
WebResourceResponseMsg::Start(webresource_response) => {
|
||||
response_received = true;
|
||||
let timing = context.timing.lock().unwrap().clone();
|
||||
let mut res = Response::new(webresource_response.url.clone(), timing);
|
||||
res.headers = webresource_response.headers;
|
||||
res.status = HttpStatus::new(
|
||||
webresource_response.status_code,
|
||||
webresource_response.status_message,
|
||||
);
|
||||
*res.body.lock().unwrap() = ResponseBody::Receiving(Vec::new());
|
||||
*response = Some(res);
|
||||
},
|
||||
WebResourceResponseMsg::Body(data) => {
|
||||
if !response_received {
|
||||
panic!("Receive body before initializing a Response!");
|
||||
}
|
||||
if let Some(ref mut res) = *response {
|
||||
match data {
|
||||
HttpBodyData::Chunk(chunk) => {
|
||||
if let ResponseBody::Receiving(ref mut body) =
|
||||
*res.body.lock().unwrap()
|
||||
{
|
||||
body.extend_from_slice(&chunk);
|
||||
} else {
|
||||
panic!("Receive Playload Message when Response body is not in Receiving state!");
|
||||
}
|
||||
},
|
||||
HttpBodyData::Done => {
|
||||
let mut res_body = res.body.lock().unwrap();
|
||||
if let ResponseBody::Receiving(ref mut body) = *res_body {
|
||||
let completed_body = std::mem::take(body);
|
||||
*res_body = ResponseBody::Done(completed_body);
|
||||
} else {
|
||||
panic!("Receive Done Message when Response body is not in Receiving state!");
|
||||
}
|
||||
break;
|
||||
},
|
||||
HttpBodyData::Cancelled => {
|
||||
*response =
|
||||
Some(Response::network_error(NetworkError::LoadCancelled));
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
WebResourceResponseMsg::None => {
|
||||
// Will not intercept the response. Continue.
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
components/net/request_interceptor.rs
Normal file
86
components/net/request_interceptor.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use content_security_policy::Destination;
|
||||
use embedder_traits::{EmbedderMsg, EmbedderProxy, WebResourceRequest, WebResourceResponseMsg};
|
||||
use ipc_channel::ipc;
|
||||
use log::error;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use net_traits::NetworkError;
|
||||
|
||||
use crate::fetch::methods::FetchContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RequestInterceptor {
|
||||
embedder_proxy: EmbedderProxy,
|
||||
}
|
||||
|
||||
impl RequestInterceptor {
|
||||
pub fn new(embedder_proxy: EmbedderProxy) -> RequestInterceptor {
|
||||
RequestInterceptor { embedder_proxy }
|
||||
}
|
||||
|
||||
pub fn intercept_request(
|
||||
&self,
|
||||
request: &mut Request,
|
||||
response: &mut Option<Response>,
|
||||
context: &FetchContext,
|
||||
) {
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
let is_for_main_frame = matches!(request.destination, Destination::Document);
|
||||
let web_resource_request = WebResourceRequest {
|
||||
method: request.method.clone(),
|
||||
url: request.url().into_url(),
|
||||
headers: request.headers.clone(),
|
||||
is_for_main_frame,
|
||||
is_redirect: request.redirect_count > 0,
|
||||
};
|
||||
|
||||
self.embedder_proxy.send(EmbedderMsg::WebResourceRequested(
|
||||
request.target_webview_id,
|
||||
web_resource_request,
|
||||
sender,
|
||||
));
|
||||
|
||||
// TODO: use done_chan and run in CoreResourceThreadPool.
|
||||
let mut accumulated_body = Vec::new();
|
||||
while let Ok(message) = receiver.recv() {
|
||||
match message {
|
||||
WebResourceResponseMsg::Start(webresource_response) => {
|
||||
let timing = context.timing.lock().unwrap().clone();
|
||||
let mut response_override =
|
||||
Response::new(webresource_response.url.into(), timing);
|
||||
response_override.headers = webresource_response.headers;
|
||||
response_override.status = HttpStatus::new(
|
||||
webresource_response.status_code,
|
||||
webresource_response.status_message,
|
||||
);
|
||||
*response = Some(response_override);
|
||||
},
|
||||
WebResourceResponseMsg::SendBodyData(data) => {
|
||||
accumulated_body.push(data);
|
||||
},
|
||||
WebResourceResponseMsg::FinishLoad => {
|
||||
if accumulated_body.is_empty() {
|
||||
break;
|
||||
}
|
||||
let Some(response) = response.as_mut() else {
|
||||
error!("Received unexpected FinishLoad message");
|
||||
break;
|
||||
};
|
||||
*response.body.lock().unwrap() =
|
||||
ResponseBody::Done(accumulated_body.into_iter().flatten().collect());
|
||||
break;
|
||||
},
|
||||
WebResourceResponseMsg::CancelLoad => {
|
||||
*response = Some(Response::network_error(NetworkError::LoadCancelled));
|
||||
break;
|
||||
},
|
||||
WebResourceResponseMsg::DoNotIntercept => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ use crate::hsts::HstsList;
|
|||
use crate::http_cache::HttpCache;
|
||||
use crate::http_loader::{http_redirect_fetch, HttpState};
|
||||
use crate::protocols::ProtocolRegistry;
|
||||
use crate::request_intercepter::RequestIntercepter;
|
||||
use crate::request_interceptor::RequestInterceptor;
|
||||
use crate::storage_thread::StorageThreadFactory;
|
||||
use crate::websocket_loader;
|
||||
|
||||
|
@ -553,7 +553,7 @@ pub struct CoreResourceManager {
|
|||
devtools_sender: Option<Sender<DevtoolsControlMsg>>,
|
||||
sw_managers: HashMap<ImmutableOrigin, IpcSender<CustomResponseMediator>>,
|
||||
filemanager: FileManager,
|
||||
request_intercepter: RequestIntercepter,
|
||||
request_interceptor: RequestInterceptor,
|
||||
thread_pool: Arc<CoreResourceThreadPool>,
|
||||
ca_certificates: CACertificates,
|
||||
ignore_certificate_errors: bool,
|
||||
|
@ -706,7 +706,7 @@ impl CoreResourceManager {
|
|||
devtools_sender,
|
||||
sw_managers: Default::default(),
|
||||
filemanager: FileManager::new(embedder_proxy.clone(), Arc::downgrade(&pool_handle)),
|
||||
request_intercepter: RequestIntercepter::new(embedder_proxy),
|
||||
request_interceptor: RequestInterceptor::new(embedder_proxy),
|
||||
thread_pool: pool_handle,
|
||||
ca_certificates,
|
||||
ignore_certificate_errors,
|
||||
|
@ -749,7 +749,7 @@ impl CoreResourceManager {
|
|||
let ua = self.user_agent.clone();
|
||||
let dc = self.devtools_sender.clone();
|
||||
let filemanager = self.filemanager.clone();
|
||||
let request_intercepter = self.request_intercepter.clone();
|
||||
let request_interceptor = self.request_interceptor.clone();
|
||||
|
||||
let timing_type = match request_builder.destination {
|
||||
Destination::Document => ResourceTimingType::Navigation,
|
||||
|
@ -791,7 +791,7 @@ impl CoreResourceManager {
|
|||
devtools_chan: dc.map(|dc| Arc::new(Mutex::new(dc))),
|
||||
filemanager: Arc::new(Mutex::new(filemanager)),
|
||||
file_token,
|
||||
request_intercepter: Arc::new(Mutex::new(request_intercepter)),
|
||||
request_interceptor: Arc::new(Mutex::new(request_interceptor)),
|
||||
cancellation_listener,
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(request.timing_type()))),
|
||||
protocols,
|
||||
|
|
|
@ -30,7 +30,7 @@ use net::fetch::methods::{self, FetchContext};
|
|||
use net::filemanager_thread::FileManager;
|
||||
use net::hsts::HstsEntry;
|
||||
use net::protocols::ProtocolRegistry;
|
||||
use net::request_intercepter::RequestIntercepter;
|
||||
use net::request_interceptor::RequestInterceptor;
|
||||
use net::resource_thread::CoreResourceThreadPool;
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
|
@ -708,7 +708,7 @@ fn test_fetch_with_hsts() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -768,7 +768,7 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -830,7 +830,7 @@ fn test_fetch_self_signed() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -1363,17 +1363,13 @@ fn test_fetch_request_intercepted() {
|
|||
.status_message(STATUS_MESSAGE.to_vec());
|
||||
let msg = embedder_traits::WebResourceResponseMsg::Start(response);
|
||||
let _ = response_sender.send(msg);
|
||||
let msg2 = embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Chunk(BODY_PART1.to_vec()),
|
||||
);
|
||||
let msg2 =
|
||||
embedder_traits::WebResourceResponseMsg::SendBodyData(BODY_PART1.to_vec());
|
||||
let _ = response_sender.send(msg2);
|
||||
let msg3 = embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Chunk(BODY_PART2.to_vec()),
|
||||
);
|
||||
let msg3 =
|
||||
embedder_traits::WebResourceResponseMsg::SendBodyData(BODY_PART2.to_vec());
|
||||
let _ = response_sender.send(msg3);
|
||||
let _ = response_sender.send(embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Done,
|
||||
));
|
||||
let _ = response_sender.send(embedder_traits::WebResourceResponseMsg::FinishLoad);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -1388,7 +1384,7 @@ fn test_fetch_request_intercepted() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
|
|
@ -42,7 +42,7 @@ use net::fetch::cors_cache::CorsCache;
|
|||
use net::fetch::methods::{self, FetchContext};
|
||||
use net::filemanager_thread::FileManager;
|
||||
use net::protocols::ProtocolRegistry;
|
||||
use net::request_intercepter::RequestIntercepter;
|
||||
use net::request_interceptor::RequestInterceptor;
|
||||
use net::resource_thread::CoreResourceThreadPool;
|
||||
use net::test::HttpState;
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
|
@ -177,7 +177,7 @@ fn new_fetch_context(
|
|||
pool_handle.unwrap_or_else(|| Weak::new()),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(sender))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(sender))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue