1use std::sync::atomic::AtomicBool;
8
9use http::HeaderMap;
10use hyper_serde::Serde;
11use malloc_size_of_derive::MallocSizeOf;
12use parking_lot::Mutex;
13use serde::{Deserialize, Serialize};
14use servo_arc::Arc;
15use servo_url::ServoUrl;
16
17use crate::fetch::headers::extract_mime_type_as_mime;
18use crate::http_status::HttpStatus;
19use crate::resource_fetch_timing::{ResourceFetchTimingContainer, ResourceTimingType};
20use crate::{
21 FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
22 TlsSecurityInfo,
23};
24
25#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
27pub enum ResponseType {
28 Basic,
29 Cors,
30 Default,
31 Error(NetworkError),
32 Opaque,
33 OpaqueRedirect,
34}
35
36#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
38pub enum TerminationReason {
39 EndUserAbort,
40 Fatal,
41 Timeout,
42}
43
44#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
47pub enum ResponseBody {
48 Empty, #[serde(with = "serde_bytes")]
50 Receiving(Vec<u8>),
51 #[serde(with = "serde_bytes")]
52 Done(Vec<u8>),
53}
54
55impl ResponseBody {
56 pub fn is_done(&self) -> bool {
57 match *self {
58 ResponseBody::Done(..) => true,
59 ResponseBody::Empty | ResponseBody::Receiving(..) => false,
60 }
61 }
62}
63
64#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
66pub enum RedirectTaint {
67 #[default]
68 SameOrigin,
69 SameSite,
70 CrossSite,
71}
72
73#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
75pub enum CacheState {
76 None,
77 Local,
78 Validated,
79 Partial,
80}
81
82#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
83pub struct ResponseInit {
84 pub url: ServoUrl,
85 #[serde(
86 deserialize_with = "::hyper_serde::deserialize",
87 serialize_with = "::hyper_serde::serialize"
88 )]
89 pub headers: HeaderMap,
90 pub status_code: u16,
91 pub referrer: Option<ServoUrl>,
92 pub location_url: Option<Result<ServoUrl, String>>,
93}
94
95#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
97pub struct Response {
98 pub response_type: ResponseType,
99 pub termination_reason: Option<TerminationReason>,
100 url: Option<ServoUrl>,
101 pub url_list: Vec<ServoUrl>,
102 pub status: HttpStatus,
103 #[serde(
104 deserialize_with = "::hyper_serde::deserialize",
105 serialize_with = "::hyper_serde::serialize"
106 )]
107 pub headers: HeaderMap,
108 #[conditional_malloc_size_of]
109 pub body: Arc<Mutex<ResponseBody>>,
110 pub cache_state: CacheState,
111 pub tls_security_info: Option<TlsSecurityInfo>,
112 pub referrer: Option<ServoUrl>,
113 pub redirect_taint: RedirectTaint,
115 pub referrer_policy: ReferrerPolicy,
116 pub cors_exposed_header_name_list: Vec<String>,
118 pub location_url: Option<Result<ServoUrl, String>>,
120 pub internal_response: Option<Box<Response>>,
123 pub return_internal: bool,
125 #[conditional_malloc_size_of]
127 pub aborted: Arc<AtomicBool>,
128 pub resource_timing: ResourceFetchTimingContainer,
130
131 pub range_requested: bool,
133
134 pub request_includes_credentials: bool,
137}
138
139impl Response {
140 pub fn new(url: ServoUrl, resource_timing: ResourceFetchTiming) -> Response {
141 Response {
142 response_type: ResponseType::Default,
143 termination_reason: None,
144 url: Some(url),
145 url_list: vec![],
146 status: HttpStatus::default(),
147 headers: HeaderMap::new(),
148 body: Arc::new(Mutex::new(ResponseBody::Empty)),
149 cache_state: CacheState::None,
150 tls_security_info: None,
151 referrer: None,
152 referrer_policy: ReferrerPolicy::EmptyString,
153 cors_exposed_header_name_list: vec![],
154 location_url: None,
155 internal_response: None,
156 return_internal: true,
157 aborted: Arc::new(AtomicBool::new(false)),
158 resource_timing: resource_timing.into(),
159 range_requested: false,
160 request_includes_credentials: true,
161 redirect_taint: Default::default(),
162 }
163 }
164
165 pub fn from_init(init: ResponseInit, resource_timing_type: ResourceTimingType) -> Response {
166 let mut res = Response::new(init.url, ResourceFetchTiming::new(resource_timing_type));
167 res.location_url = init.location_url;
168 res.headers = init.headers;
169 res.referrer = init.referrer;
170 res.status = HttpStatus::new_raw(init.status_code, vec![]);
171 res
172 }
173
174 pub fn network_error(e: NetworkError) -> Response {
175 Response {
176 response_type: ResponseType::Error(e),
177 termination_reason: None,
178 url: None,
179 url_list: vec![],
180 status: HttpStatus::new_error(),
181 headers: HeaderMap::new(),
182 body: Arc::new(Mutex::new(ResponseBody::Empty)),
183 cache_state: CacheState::None,
184 tls_security_info: None,
185 referrer: None,
186 referrer_policy: ReferrerPolicy::EmptyString,
187 cors_exposed_header_name_list: vec![],
188 location_url: None,
189 internal_response: None,
190 return_internal: true,
191 aborted: Arc::new(AtomicBool::new(false)),
192 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Error).into(),
193 range_requested: false,
194 request_includes_credentials: true,
195 redirect_taint: Default::default(),
196 }
197 }
198
199 pub fn url(&self) -> Option<&ServoUrl> {
200 self.url.as_ref()
201 }
202
203 pub fn is_network_error(&self) -> bool {
204 matches!(self.response_type, ResponseType::Error(..))
205 }
206
207 pub fn get_network_error(&self) -> Option<&NetworkError> {
208 match self.response_type {
209 ResponseType::Error(ref e) => Some(e),
210 _ => None,
211 }
212 }
213
214 pub fn set_network_error(&mut self, network_error: NetworkError) {
215 self.response_type = ResponseType::Error(network_error);
216 }
217
218 pub fn actual_response(&self) -> &Response {
219 match &self.internal_response {
220 Some(internal_response) if self.return_internal => internal_response,
221 _ => self,
222 }
223 }
224
225 #[expect(
226 clippy::unnecessary_unwrap,
227 reason = "match doesn't work, the borrow checker is overly conservative about &mut here"
228 )]
229 pub fn actual_response_mut(&mut self) -> &mut Response {
230 if self.return_internal && self.internal_response.is_some() {
231 self.internal_response.as_mut().unwrap()
232 } else {
233 self
234 }
235 }
236
237 pub fn to_actual(self) -> Response {
238 match self.internal_response {
239 Some(internal_response) if self.return_internal => *internal_response,
240 _ => self,
241 }
242 }
243
244 pub fn get_resource_timing(&self) -> &ResourceFetchTimingContainer {
245 &self.resource_timing
246 }
247
248 pub fn to_filtered(self, filter_type: ResponseType) -> Response {
251 assert!(!matches!(
252 filter_type,
253 ResponseType::Default | ResponseType::Error(..)
254 ));
255
256 let old_response = self.to_actual();
257
258 if let ResponseType::Error(e) = old_response.response_type {
259 return Response::network_error(e);
260 }
261
262 let old_headers = old_response.headers.clone();
263 let exposed_headers = old_response.cors_exposed_header_name_list.clone();
264 let mut response = old_response.clone();
265 response.internal_response = Some(Box::new(old_response));
266 response.response_type = filter_type;
267
268 match response.response_type {
269 ResponseType::Default | ResponseType::Error(..) => unreachable!(),
270
271 ResponseType::Basic => {
272 let headers = old_headers
273 .iter()
274 .filter(|(name, _)| {
275 let name = name.as_str();
276 !name.eq_ignore_ascii_case("set-cookie") &&
277 !name.eq_ignore_ascii_case("set-cookie2")
278 })
279 .map(|(n, v)| (n.clone(), v.clone()))
280 .collect();
281 response.headers = headers;
282 },
283
284 ResponseType::Cors => {
285 let headers = old_headers
286 .iter()
287 .filter(|(name, _)| {
288 let name = name.as_str();
289 if name.eq_ignore_ascii_case("cache-control") ||
290 name.eq_ignore_ascii_case("content-language") ||
291 name.eq_ignore_ascii_case("content-length") ||
292 name.eq_ignore_ascii_case("content-type") ||
293 name.eq_ignore_ascii_case("expires") ||
294 name.eq_ignore_ascii_case("last-modified") ||
295 name.eq_ignore_ascii_case("pragma")
296 {
297 true
298 } else if name.eq_ignore_ascii_case("set-cookie") ||
299 name.eq_ignore_ascii_case("set-cookie2")
300 {
301 false
302 } else {
303 exposed_headers
304 .iter()
305 .any(|h| h.as_str().eq_ignore_ascii_case(name))
306 }
307 })
308 .map(|(n, v)| (n.clone(), v.clone()))
309 .collect();
310 response.headers = headers;
311 },
312
313 ResponseType::Opaque => {
314 response.url_list = vec![];
315 response.url = None;
316 response.headers = HeaderMap::new();
317 response.status = HttpStatus::new_error();
318 response.body = Arc::new(Mutex::new(ResponseBody::Empty));
319 response.cache_state = CacheState::None;
320 },
321
322 ResponseType::OpaqueRedirect => {
323 response.headers = HeaderMap::new();
324 response.status = HttpStatus::new_error();
325 response.body = Arc::new(Mutex::new(ResponseBody::Empty));
326 response.cache_state = CacheState::None;
327 },
328 }
329
330 response
331 }
332
333 pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> {
334 fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata {
335 let mut metadata = Metadata::default(url.clone());
336 metadata.set_content_type(extract_mime_type_as_mime(&response.headers).as_ref());
337 metadata.location_url.clone_from(&response.location_url);
338 metadata.headers = Some(Serde(response.headers.clone()));
339 metadata.status.clone_from(&response.status);
340 metadata.referrer.clone_from(&response.referrer);
341 metadata.referrer_policy = response.referrer_policy;
342 metadata.redirected = response.actual_response().url_list.len() > 1;
343 metadata
344 .tls_security_info
345 .clone_from(&response.tls_security_info);
346 metadata
347 }
348
349 if let Some(error) = self.get_network_error() {
350 return Err(error.clone());
351 }
352
353 let metadata = self.url.as_ref().map(|url| init_metadata(self, url));
354
355 if let Some(ref response) = self.internal_response {
356 match response.url {
357 Some(ref url) => {
358 let unsafe_metadata = init_metadata(response, url);
359
360 match self.response_type {
361 ResponseType::Basic => Ok(FetchMetadata::Filtered {
362 filtered: FilteredMetadata::Basic(metadata.unwrap()),
363 unsafe_: unsafe_metadata,
364 }),
365 ResponseType::Cors => Ok(FetchMetadata::Filtered {
366 filtered: FilteredMetadata::Cors(metadata.unwrap()),
367 unsafe_: unsafe_metadata,
368 }),
369 ResponseType::Default => unreachable!(),
370 ResponseType::Error(ref network_err) => Err(network_err.clone()),
371 ResponseType::Opaque => Ok(FetchMetadata::Filtered {
372 filtered: FilteredMetadata::Opaque,
373 unsafe_: unsafe_metadata,
374 }),
375 ResponseType::OpaqueRedirect => Ok(FetchMetadata::Filtered {
376 filtered: FilteredMetadata::OpaqueRedirect(url.clone()),
377 unsafe_: unsafe_metadata,
378 }),
379 }
380 },
381 None => Err(NetworkError::ResourceLoadError(
382 "No url found in unsafe response".to_owned(),
383 )),
384 }
385 } else {
386 assert_eq!(self.response_type, ResponseType::Default);
387 Ok(FetchMetadata::Unfiltered(metadata.unwrap()))
388 }
389 }
390}