1use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7use bytes::Bytes;
8use crossbeam_channel::Sender;
9use devtools_traits::{
10 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
11 HttpResponse as DevtoolsHttpResponse, NetworkEvent, SecurityInfoUpdate,
12};
13use http::{HeaderMap, Method};
14use hyper_serde::Serde;
15use log::error;
16use net_traits::FetchMetadata;
17use net_traits::http_status::HttpStatus;
18use net_traits::request::{Destination, Request};
19use net_traits::response::{CacheState, Response};
20use servo_base::id::{BrowsingContextId, PipelineId};
21use servo_url::ServoUrl;
22
23use crate::fetch::methods::FetchContext;
24
25#[expect(clippy::too_many_arguments)]
26pub(crate) fn prepare_devtools_request(
27 request_id: String,
28 url: ServoUrl,
29 method: Method,
30 headers: HeaderMap,
31 body: Option<Vec<u8>>,
32 pipeline_id: PipelineId,
33 connect_time: Duration,
34 send_time: Duration,
35 destination: Destination,
36 is_xhr: bool,
37 browsing_context_id: BrowsingContextId,
38) -> ChromeToDevtoolsControlMsg {
39 let started_date_time = SystemTime::now();
40 let request = DevtoolsHttpRequest {
41 url,
42 method,
43 headers,
44 body: body.map(Bytes::from),
45 pipeline_id,
46 started_date_time,
47 time_stamp: started_date_time
48 .duration_since(UNIX_EPOCH)
49 .unwrap_or_default()
50 .as_secs() as i64,
51 connect_time,
52 send_time,
53 destination,
54 is_xhr,
55 browsing_context_id,
56 };
57 let net_event = NetworkEvent::HttpRequestUpdate(request);
58
59 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
60}
61
62pub(crate) fn send_request_to_devtools(
63 msg: ChromeToDevtoolsControlMsg,
64 devtools_chan: &Sender<DevtoolsControlMsg>,
65) {
66 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
67 {
68 return;
69 }
70 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
71 error!("DevTools send failed: {e}");
72 }
73}
74
75pub(crate) fn send_response_to_devtools(
76 request: &Request,
77 context: &FetchContext,
78 response: &Response,
79 body_data: Option<Vec<u8>>,
80) {
81 let meta = match response.metadata() {
82 Ok(FetchMetadata::Unfiltered(m)) => m,
83 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
84 Err(_) => {
85 log::warn!("No metadata available, skipping devtools response.");
86 return;
87 },
88 };
89 send_response_values_to_devtools(
90 meta.headers.map(Serde::into_inner),
91 meta.status,
92 body_data,
93 response.cache_state,
94 request,
95 context.devtools_chan.clone(),
96 );
97}
98
99pub(crate) fn send_response_values_to_devtools(
100 headers: Option<HeaderMap>,
101 status: HttpStatus,
102 body: Option<Vec<u8>>,
103 cache_state: CacheState,
104 request: &Request,
105 devtools_chan: Option<Sender<DevtoolsControlMsg>>,
106) {
107 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
108 devtools_chan,
109 request.pipeline_id,
110 request.target_webview_id,
111 ) {
112 let browsing_context_id = webview_id.into();
113 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
114
115 let devtoolsresponse = DevtoolsHttpResponse {
116 headers,
117 status,
118 body: body.map(Bytes::from),
119 from_cache,
120 pipeline_id,
121 browsing_context_id,
122 };
123 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
124
125 let msg =
126 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
127
128 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
129 }
130}
131
132pub(crate) fn send_security_info_to_devtools(
133 request: &Request,
134 context: &FetchContext,
135 response: &Response,
136) {
137 let meta = match response.metadata() {
138 Ok(FetchMetadata::Unfiltered(m)) => m,
139 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
140 Err(_) => {
141 log::warn!("No metadata available, skipping devtools security info.");
142 return;
143 },
144 };
145
146 if let (Some(devtools_chan), Some(security_info), Some(webview_id)) = (
147 context.devtools_chan.clone(),
148 meta.tls_security_info,
149 request.target_webview_id,
150 ) {
151 let update = NetworkEvent::SecurityInfo(SecurityInfoUpdate {
152 browsing_context_id: webview_id.into(),
153 security_info: Some(security_info),
154 });
155
156 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), update);
157
158 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
159 }
160}
161
162pub(crate) fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
163 if request.url().scheme() == "data" {
165 return;
166 }
167 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
168 context.devtools_chan.as_ref(),
169 request.target_webview_id.map(|id| id.into()),
170 request.pipeline_id,
171 ) {
172 let devtools_request = DevtoolsHttpRequest {
174 url: request.current_url(),
175 method: request.method.clone(),
176 headers: request.headers.clone(),
177 body: None,
178 pipeline_id,
179 started_date_time: SystemTime::now(),
180 time_stamp: 0,
181 connect_time: Duration::from_millis(0),
182 send_time: Duration::from_millis(0),
183 destination: request.destination,
184 is_xhr: false,
185 browsing_context_id,
186 };
187
188 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
189 request.id.0.to_string(),
190 NetworkEvent::HttpRequest(devtools_request),
191 );
192
193 send_request_to_devtools(msg, devtools_chan);
194 }
195}