servo/components/net/request_interceptor.rs
Jonathan Schwender 66d9f957e6
EmbedderMsg: port reply channels to GenericChannel (#39018)
This change ports all `EmbedderMsg` reply channels that don't use the
`ROUTER` to GenericChannel.
The remaining reply channels that use the router are blocked until
#38973 is merged.
This is a breaking change in the API between libservo and embedders.

Future work: A lot of the reply channels in this PR look like they
conceptually should be oneshot ipc channels. It might make sense to
provide a `OneshotGenericChannel` abstraction that encodes this.

Testing: No functional changes - covered by existing tests. None of the
channels changed here uses the Router
Part of #38912

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-29 12:44:21 +00:00

86 lines
3.3 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 base::generic_channel;
use content_security_policy::Destination;
use embedder_traits::{EmbedderMsg, EmbedderProxy, 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::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) = generic_channel::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,
}
}
}
}