Skip to main content

net/
devtools.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#[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        // Do not forward data requests to devtools
197        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            // Build the partial DevtoolsHttpRequest
206            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}