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