1use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7use crossbeam_channel::Sender;
8use devtools_traits::{
9 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
10 HttpResponse as DevtoolsHttpResponse, NetworkEvent, SecurityInfoUpdate,
11};
12use http::{HeaderMap, Method};
13use hyper_serde::Serde;
14use log::error;
15use net_traits::http_status::HttpStatus;
16use net_traits::request::{Destination, Request};
17use net_traits::response::{CacheState, Response};
18use net_traits::{DebugVec, FetchMetadata};
19use servo_base::id::{BrowsingContextId, PipelineId};
20use servo_url::ServoUrl;
21
22use crate::fetch::methods::FetchContext;
23
24#[allow(clippy::too_many_arguments)]
25pub(crate) fn prepare_devtools_request(
26 request_id: String,
27 url: ServoUrl,
28 method: Method,
29 headers: HeaderMap,
30 body: Option<Vec<u8>>,
31 pipeline_id: PipelineId,
32 connect_time: Duration,
33 send_time: Duration,
34 destination: Destination,
35 is_xhr: bool,
36 browsing_context_id: BrowsingContextId,
37) -> ChromeToDevtoolsControlMsg {
38 let started_date_time = SystemTime::now();
39 let request = DevtoolsHttpRequest {
40 url,
41 method,
42 headers,
43 body: body.map(DebugVec::from),
44 pipeline_id,
45 started_date_time,
46 time_stamp: started_date_time
47 .duration_since(UNIX_EPOCH)
48 .unwrap_or_default()
49 .as_secs() as i64,
50 connect_time,
51 send_time,
52 destination,
53 is_xhr,
54 browsing_context_id,
55 };
56 let net_event = NetworkEvent::HttpRequestUpdate(request);
57
58 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
59}
60
61pub(crate) fn send_request_to_devtools(
62 msg: ChromeToDevtoolsControlMsg,
63 devtools_chan: &Sender<DevtoolsControlMsg>,
64) {
65 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
66 {
67 return;
68 }
69 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
70 error!("DevTools send failed: {e}");
71 }
72}
73
74pub(crate) fn send_response_to_devtools(
75 request: &Request,
76 context: &FetchContext,
77 response: &Response,
78 body_data: Option<Vec<u8>>,
79) {
80 let meta = match response.metadata() {
81 Ok(FetchMetadata::Unfiltered(m)) => m,
82 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
83 Err(_) => {
84 log::warn!("No metadata available, skipping devtools response.");
85 return;
86 },
87 };
88 send_response_values_to_devtools(
89 meta.headers.map(Serde::into_inner),
90 meta.status,
91 body_data,
92 response.cache_state,
93 request,
94 context.devtools_chan.clone(),
95 );
96}
97
98#[allow(clippy::too_many_arguments)]
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(DebugVec::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}