Files
servo/components/net/request_interceptor.rs
Josh Matthews 872b7ba485 net: Convert blocking embedder communication to async (#41965)
#41857 exposed some more code in the net crate that performed blocking
communication with the embedder and could prevent other networking tasks
from running. To address that, we need the net code to perform async
receive operations, which requires passing tokio channels to the
embedder.

However, the current embedding message design puts all messages in the
same enum and requires that they are serializable. Embedder messages
from the network do not require this property, since they run in the
same process as the embedder. Therefore, this PR creates a new
EmbedderProxy structure that is generic over the message, allowing each
component of Servo to use a embedder message type that is specific to
that component.

The final benefit of this set of changes is that the embedder messages
for a particular crate can now live in the crate itself (since the only
crate that depends on it is the servo crate), not in the shared
`embedding_traits` crate. This hugely reduces the amount of code that
needs to be rebuilt when changing these messages, enabling much faster
incremental builds for those changes.

Testing: Strictly refactoring; existing test coverage is sufficient.
Fixes: #41958

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2026-01-21 19:49:01 +00:00

88 lines
3.4 KiB
Rust

/* 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::{GenericEmbedderProxy, WebResourceRequest, WebResourceResponseMsg};
use log::error;
use net_traits::NetworkError;
use net_traits::http_status::HttpStatus;
use net_traits::request::Request;
use net_traits::response::{Response, ResponseBody};
use crate::embedder::NetToEmbedderMsg;
use crate::fetch::methods::FetchContext;
#[derive(Clone)]
pub struct RequestInterceptor {
embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
}
impl RequestInterceptor {
pub fn new(embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>) -> RequestInterceptor {
RequestInterceptor { embedder_proxy }
}
pub async fn intercept_request(
&self,
request: &mut Request,
response: &mut Option<Response>,
context: &FetchContext,
) {
let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel();
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(NetToEmbedderMsg::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 Some(message) = receiver.recv().await {
match message {
WebResourceResponseMsg::Start(webresource_response) => {
let timing = context.timing.lock().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() =
ResponseBody::Done(accumulated_body.into_iter().flatten().collect());
break;
},
WebResourceResponseMsg::CancelLoad => {
*response = Some(Response::network_error(NetworkError::LoadCancelled));
break;
},
WebResourceResponseMsg::DoNotIntercept => break,
}
}
}
}